diff options
Diffstat (limited to 'crates/project_model/src')
-rw-r--r-- | crates/project_model/src/build_data.rs | 1 | ||||
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 101 | ||||
-rw-r--r-- | crates/project_model/src/rustc_cfg.rs | 45 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 46 |
4 files changed, 143 insertions, 50 deletions
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index 7b88dca63..3aa546980 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -143,6 +143,7 @@ impl WorkspaceBuildData { | |||
143 | cmd.env("RA_RUSTC_WRAPPER", "1"); | 143 | cmd.env("RA_RUSTC_WRAPPER", "1"); |
144 | } | 144 | } |
145 | 145 | ||
146 | cmd.current_dir(cargo_toml.parent().unwrap()); | ||
146 | cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"]) | 147 | cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"]) |
147 | .arg(cargo_toml.as_ref()); | 148 | .arg(cargo_toml.as_ref()); |
148 | 149 | ||
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index bc6e20341..4a4996cf4 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -119,6 +119,32 @@ pub struct RustAnalyzerPackageMetaData { | |||
119 | pub struct PackageDependency { | 119 | pub struct PackageDependency { |
120 | pub pkg: Package, | 120 | pub pkg: Package, |
121 | pub name: String, | 121 | pub name: String, |
122 | pub kind: DepKind, | ||
123 | } | ||
124 | |||
125 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
126 | pub enum DepKind { | ||
127 | /// Available to the library, binary, and dev targets in the package (but not the build script). | ||
128 | Normal, | ||
129 | /// Available only to test and bench targets (and the library target, when built with `cfg(test)`). | ||
130 | Dev, | ||
131 | /// Available only to the build script target. | ||
132 | Build, | ||
133 | } | ||
134 | |||
135 | impl DepKind { | ||
136 | fn new(list: &[cargo_metadata::DepKindInfo]) -> Self { | ||
137 | for info in list { | ||
138 | match info.kind { | ||
139 | cargo_metadata::DependencyKind::Normal => return Self::Normal, | ||
140 | cargo_metadata::DependencyKind::Development => return Self::Dev, | ||
141 | cargo_metadata::DependencyKind::Build => return Self::Build, | ||
142 | cargo_metadata::DependencyKind::Unknown => continue, | ||
143 | } | ||
144 | } | ||
145 | |||
146 | Self::Normal | ||
147 | } | ||
122 | } | 148 | } |
123 | 149 | ||
124 | /// Information associated with a package's target | 150 | /// Information associated with a package's target |
@@ -144,6 +170,7 @@ pub enum TargetKind { | |||
144 | Example, | 170 | Example, |
145 | Test, | 171 | Test, |
146 | Bench, | 172 | Bench, |
173 | BuildScript, | ||
147 | Other, | 174 | Other, |
148 | } | 175 | } |
149 | 176 | ||
@@ -155,6 +182,7 @@ impl TargetKind { | |||
155 | "test" => TargetKind::Test, | 182 | "test" => TargetKind::Test, |
156 | "bench" => TargetKind::Bench, | 183 | "bench" => TargetKind::Bench, |
157 | "example" => TargetKind::Example, | 184 | "example" => TargetKind::Example, |
185 | "custom-build" => TargetKind::BuildScript, | ||
158 | "proc-macro" => TargetKind::Lib, | 186 | "proc-macro" => TargetKind::Lib, |
159 | _ if kind.contains("lib") => TargetKind::Lib, | 187 | _ if kind.contains("lib") => TargetKind::Lib, |
160 | _ => continue, | 188 | _ => continue, |
@@ -201,31 +229,12 @@ impl CargoWorkspace { | |||
201 | if let Some(parent) = cargo_toml.parent() { | 229 | if let Some(parent) = cargo_toml.parent() { |
202 | meta.current_dir(parent.to_path_buf()); | 230 | meta.current_dir(parent.to_path_buf()); |
203 | } | 231 | } |
204 | let target = if let Some(target) = config.target.as_ref() { | 232 | let target = if let Some(target) = &config.target { |
205 | Some(target.clone()) | 233 | Some(target.clone()) |
234 | } else if let stdout @ Some(_) = cargo_config_build_target(cargo_toml) { | ||
235 | stdout | ||
206 | } else { | 236 | } else { |
207 | // cargo metadata defaults to giving information for _all_ targets. | 237 | rustc_discover_host_triple(cargo_toml) |
208 | // In the absence of a preference from the user, we use the host platform. | ||
209 | let mut rustc = Command::new(toolchain::rustc()); | ||
210 | rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV"); | ||
211 | log::debug!("Discovering host platform by {:?}", rustc); | ||
212 | match utf8_stdout(rustc) { | ||
213 | Ok(stdout) => { | ||
214 | let field = "host: "; | ||
215 | let target = stdout.lines().find_map(|l| l.strip_prefix(field)); | ||
216 | if let Some(target) = target { | ||
217 | Some(target.to_string()) | ||
218 | } else { | ||
219 | // If we fail to resolve the host platform, it's not the end of the world. | ||
220 | log::info!("rustc -vV did not report host platform, got:\n{}", stdout); | ||
221 | None | ||
222 | } | ||
223 | } | ||
224 | Err(e) => { | ||
225 | log::warn!("Failed to discover host platform: {}", e); | ||
226 | None | ||
227 | } | ||
228 | } | ||
229 | }; | 238 | }; |
230 | if let Some(target) = target { | 239 | if let Some(target) = target { |
231 | meta.other_options(vec![String::from("--filter-platform"), target]); | 240 | meta.other_options(vec![String::from("--filter-platform"), target]); |
@@ -320,7 +329,11 @@ impl CargoWorkspace { | |||
320 | continue; | 329 | continue; |
321 | } | 330 | } |
322 | }; | 331 | }; |
323 | let dep = PackageDependency { name: dep_node.name, pkg }; | 332 | let dep = PackageDependency { |
333 | name: dep_node.name, | ||
334 | pkg, | ||
335 | kind: DepKind::new(&dep_node.dep_kinds), | ||
336 | }; | ||
324 | packages[source].dependencies.push(dep); | 337 | packages[source].dependencies.push(dep); |
325 | } | 338 | } |
326 | packages[source].active_features.extend(node.features); | 339 | packages[source].active_features.extend(node.features); |
@@ -368,3 +381,43 @@ impl CargoWorkspace { | |||
368 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 | 381 | self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 |
369 | } | 382 | } |
370 | } | 383 | } |
384 | |||
385 | fn rustc_discover_host_triple(cargo_toml: &AbsPath) -> Option<String> { | ||
386 | let mut rustc = Command::new(toolchain::rustc()); | ||
387 | rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV"); | ||
388 | log::debug!("Discovering host platform by {:?}", rustc); | ||
389 | match utf8_stdout(rustc) { | ||
390 | Ok(stdout) => { | ||
391 | let field = "host: "; | ||
392 | let target = stdout.lines().find_map(|l| l.strip_prefix(field)); | ||
393 | if let Some(target) = target { | ||
394 | Some(target.to_string()) | ||
395 | } else { | ||
396 | // If we fail to resolve the host platform, it's not the end of the world. | ||
397 | log::info!("rustc -vV did not report host platform, got:\n{}", stdout); | ||
398 | None | ||
399 | } | ||
400 | } | ||
401 | Err(e) => { | ||
402 | log::warn!("Failed to discover host platform: {}", e); | ||
403 | None | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | |||
408 | fn cargo_config_build_target(cargo_toml: &AbsPath) -> Option<String> { | ||
409 | let mut cargo_config = Command::new(toolchain::cargo()); | ||
410 | cargo_config | ||
411 | .current_dir(cargo_toml.parent().unwrap()) | ||
412 | .args(&["-Z", "unstable-options", "config", "get", "build.target"]) | ||
413 | .env("RUSTC_BOOTSTRAP", "1"); | ||
414 | // if successful we receive `build.target = "target-triple"` | ||
415 | log::debug!("Discovering cargo config target by {:?}", cargo_config); | ||
416 | match utf8_stdout(cargo_config) { | ||
417 | Ok(stdout) => stdout | ||
418 | .strip_prefix("build.target = \"") | ||
419 | .and_then(|stdout| stdout.strip_suffix('"')) | ||
420 | .map(ToOwned::to_owned), | ||
421 | Err(_) => None, | ||
422 | } | ||
423 | } | ||
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs index 312708575..012eab256 100644 --- a/crates/project_model/src/rustc_cfg.rs +++ b/crates/project_model/src/rustc_cfg.rs | |||
@@ -2,9 +2,12 @@ | |||
2 | 2 | ||
3 | use std::process::Command; | 3 | use std::process::Command; |
4 | 4 | ||
5 | use anyhow::Result; | ||
6 | use paths::AbsPath; | ||
7 | |||
5 | use crate::{cfg_flag::CfgFlag, utf8_stdout}; | 8 | use crate::{cfg_flag::CfgFlag, utf8_stdout}; |
6 | 9 | ||
7 | pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { | 10 | pub(crate) fn get(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Vec<CfgFlag> { |
8 | let _p = profile::span("rustc_cfg::get"); | 11 | let _p = profile::span("rustc_cfg::get"); |
9 | let mut res = Vec::with_capacity(6 * 2 + 1); | 12 | let mut res = Vec::with_capacity(6 * 2 + 1); |
10 | 13 | ||
@@ -16,19 +19,39 @@ pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { | |||
16 | } | 19 | } |
17 | } | 20 | } |
18 | 21 | ||
19 | let rustc_cfgs = { | 22 | match get_rust_cfgs(cargo_toml, target) { |
20 | let mut cmd = Command::new(toolchain::rustc()); | ||
21 | cmd.args(&["--print", "cfg", "-O"]); | ||
22 | if let Some(target) = target { | ||
23 | cmd.args(&["--target", target]); | ||
24 | } | ||
25 | utf8_stdout(cmd) | ||
26 | }; | ||
27 | |||
28 | match rustc_cfgs { | ||
29 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), | 23 | Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), |
30 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), | 24 | Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), |
31 | } | 25 | } |
32 | 26 | ||
33 | res | 27 | res |
34 | } | 28 | } |
29 | |||
30 | fn get_rust_cfgs(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Result<String> { | ||
31 | let cargo_rust_cfgs = match cargo_toml { | ||
32 | Some(cargo_toml) => { | ||
33 | let mut cargo_config = Command::new(toolchain::cargo()); | ||
34 | cargo_config | ||
35 | .current_dir(cargo_toml.parent().unwrap()) | ||
36 | .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"]) | ||
37 | .env("RUSTC_BOOTSTRAP", "1"); | ||
38 | if let Some(target) = target { | ||
39 | cargo_config.args(&["--target", target]); | ||
40 | } | ||
41 | utf8_stdout(cargo_config).ok() | ||
42 | } | ||
43 | None => None, | ||
44 | }; | ||
45 | match cargo_rust_cfgs { | ||
46 | Some(stdout) => Ok(stdout), | ||
47 | None => { | ||
48 | // using unstable cargo features failed, fall back to using plain rustc | ||
49 | let mut cmd = Command::new(toolchain::rustc()); | ||
50 | cmd.args(&["--print", "cfg", "-O"]); | ||
51 | if let Some(target) = target { | ||
52 | cmd.args(&["--target", target]); | ||
53 | } | ||
54 | utf8_stdout(cmd) | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 2fcd0f8fa..607e62ea5 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -6,6 +6,7 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; | |||
6 | 6 | ||
7 | use anyhow::{Context, Result}; | 7 | use anyhow::{Context, Result}; |
8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
9 | use cargo_workspace::DepKind; | ||
9 | use cfg::CfgOptions; | 10 | use cfg::CfgOptions; |
10 | use paths::{AbsPath, AbsPathBuf}; | 11 | use paths::{AbsPath, AbsPathBuf}; |
11 | use proc_macro_api::ProcMacroClient; | 12 | use proc_macro_api::ProcMacroClient; |
@@ -143,7 +144,8 @@ impl ProjectWorkspace { | |||
143 | } else { | 144 | } else { |
144 | None | 145 | None |
145 | }; | 146 | }; |
146 | let rustc_cfg = rustc_cfg::get(config.target.as_deref()); | 147 | |
148 | let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); | ||
147 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } | 149 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } |
148 | } | 150 | } |
149 | }; | 151 | }; |
@@ -159,7 +161,7 @@ impl ProjectWorkspace { | |||
159 | Some(path) => Some(Sysroot::load(path)?), | 161 | Some(path) => Some(Sysroot::load(path)?), |
160 | None => None, | 162 | None => None, |
161 | }; | 163 | }; |
162 | let rustc_cfg = rustc_cfg::get(target); | 164 | let rustc_cfg = rustc_cfg::get(None, target); |
163 | Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) | 165 | Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) |
164 | } | 166 | } |
165 | 167 | ||
@@ -310,7 +312,7 @@ fn project_json_to_crate_graph( | |||
310 | 312 | ||
311 | let target_cfgs = match krate.target.as_deref() { | 313 | let target_cfgs = match krate.target.as_deref() { |
312 | Some(target) => { | 314 | Some(target) => { |
313 | cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target))) | 315 | cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target))) |
314 | } | 316 | } |
315 | None => &rustc_cfg, | 317 | None => &rustc_cfg, |
316 | }; | 318 | }; |
@@ -389,6 +391,7 @@ fn cargo_to_crate_graph( | |||
389 | &cfg_options, | 391 | &cfg_options, |
390 | proc_macro_loader, | 392 | proc_macro_loader, |
391 | file_id, | 393 | file_id, |
394 | &cargo[tgt].name, | ||
392 | ); | 395 | ); |
393 | if cargo[tgt].kind == TargetKind::Lib { | 396 | if cargo[tgt].kind == TargetKind::Lib { |
394 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | 397 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); |
@@ -405,23 +408,25 @@ fn cargo_to_crate_graph( | |||
405 | } | 408 | } |
406 | } | 409 | } |
407 | 410 | ||
408 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | 411 | pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind)); |
409 | } | 412 | } |
410 | } | 413 | } |
411 | 414 | ||
412 | // Set deps to the core, std and to the lib target of the current package | 415 | // Set deps to the core, std and to the lib target of the current package |
413 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 416 | for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { |
414 | if let Some((to, name)) = lib_tgt.clone() { | 417 | if let Some((to, name)) = lib_tgt.clone() { |
415 | if to != from { | 418 | if to != *from && *kind != TargetKind::BuildScript { |
419 | // (build script can not depend on its library target) | ||
420 | |||
416 | // For root projects with dashes in their name, | 421 | // For root projects with dashes in their name, |
417 | // cargo metadata does not do any normalization, | 422 | // cargo metadata does not do any normalization, |
418 | // so we do it ourselves currently | 423 | // so we do it ourselves currently |
419 | let name = CrateName::normalize_dashes(&name); | 424 | let name = CrateName::normalize_dashes(&name); |
420 | add_dep(&mut crate_graph, from, name, to); | 425 | add_dep(&mut crate_graph, *from, name, to); |
421 | } | 426 | } |
422 | } | 427 | } |
423 | for (name, krate) in public_deps.iter() { | 428 | for (name, krate) in public_deps.iter() { |
424 | add_dep(&mut crate_graph, from, name.clone(), *krate); | 429 | add_dep(&mut crate_graph, *from, name.clone(), *krate); |
425 | } | 430 | } |
426 | } | 431 | } |
427 | } | 432 | } |
@@ -432,8 +437,17 @@ fn cargo_to_crate_graph( | |||
432 | for dep in cargo[pkg].dependencies.iter() { | 437 | for dep in cargo[pkg].dependencies.iter() { |
433 | let name = CrateName::new(&dep.name).unwrap(); | 438 | let name = CrateName::new(&dep.name).unwrap(); |
434 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 439 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
435 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 440 | for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { |
436 | add_dep(&mut crate_graph, from, name.clone(), to) | 441 | if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript { |
442 | // Only build scripts may depend on build dependencies. | ||
443 | continue; | ||
444 | } | ||
445 | if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript { | ||
446 | // Build scripts may only depend on build dependencies. | ||
447 | continue; | ||
448 | } | ||
449 | |||
450 | add_dep(&mut crate_graph, *from, name.clone(), to) | ||
437 | } | 451 | } |
438 | } | 452 | } |
439 | } | 453 | } |
@@ -470,7 +484,7 @@ fn handle_rustc_crates( | |||
470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, | 484 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, |
471 | public_deps: &[(CrateName, CrateId)], | 485 | public_deps: &[(CrateName, CrateId)], |
472 | cargo: &CargoWorkspace, | 486 | cargo: &CargoWorkspace, |
473 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>, | 487 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>, |
474 | ) { | 488 | ) { |
475 | let mut rustc_pkg_crates = FxHashMap::default(); | 489 | let mut rustc_pkg_crates = FxHashMap::default(); |
476 | // The root package of the rustc-dev component is rustc_driver, so we match that | 490 | // The root package of the rustc-dev component is rustc_driver, so we match that |
@@ -504,6 +518,7 @@ fn handle_rustc_crates( | |||
504 | &cfg_options, | 518 | &cfg_options, |
505 | proc_macro_loader, | 519 | proc_macro_loader, |
506 | file_id, | 520 | file_id, |
521 | &rustc_workspace[tgt].name, | ||
507 | ); | 522 | ); |
508 | pkg_to_lib_crate.insert(pkg, crate_id); | 523 | pkg_to_lib_crate.insert(pkg, crate_id); |
509 | // Add dependencies on core / std / alloc for this crate | 524 | // Add dependencies on core / std / alloc for this crate |
@@ -538,13 +553,13 @@ fn handle_rustc_crates( | |||
538 | if !package.metadata.rustc_private { | 553 | if !package.metadata.rustc_private { |
539 | continue; | 554 | continue; |
540 | } | 555 | } |
541 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 556 | for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() { |
542 | // Avoid creating duplicate dependencies | 557 | // Avoid creating duplicate dependencies |
543 | // This avoids the situation where `from` depends on e.g. `arrayvec`, but | 558 | // This avoids the situation where `from` depends on e.g. `arrayvec`, but |
544 | // `rust_analyzer` thinks that it should use the one from the `rustcSource` | 559 | // `rust_analyzer` thinks that it should use the one from the `rustcSource` |
545 | // instead of the one from `crates.io` | 560 | // instead of the one from `crates.io` |
546 | if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { | 561 | if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) { |
547 | add_dep(crate_graph, from, name.clone(), to); | 562 | add_dep(crate_graph, *from, name.clone(), to); |
548 | } | 563 | } |
549 | } | 564 | } |
550 | } | 565 | } |
@@ -559,6 +574,7 @@ fn add_target_crate_root( | |||
559 | cfg_options: &CfgOptions, | 574 | cfg_options: &CfgOptions, |
560 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 575 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
561 | file_id: FileId, | 576 | file_id: FileId, |
577 | cargo_name: &str, | ||
562 | ) -> CrateId { | 578 | ) -> CrateId { |
563 | let edition = pkg.edition; | 579 | let edition = pkg.edition; |
564 | let cfg_options = { | 580 | let cfg_options = { |
@@ -585,7 +601,7 @@ fn add_target_crate_root( | |||
585 | .map(|it| proc_macro_loader(&it)) | 601 | .map(|it| proc_macro_loader(&it)) |
586 | .unwrap_or_default(); | 602 | .unwrap_or_default(); |
587 | 603 | ||
588 | let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone()); | 604 | let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); |
589 | let crate_id = crate_graph.add_crate_root( | 605 | let crate_id = crate_graph.add_crate_root( |
590 | file_id, | 606 | file_id, |
591 | edition, | 607 | edition, |