aboutsummaryrefslogtreecommitdiff
path: root/crates/project_model
diff options
context:
space:
mode:
Diffstat (limited to 'crates/project_model')
-rw-r--r--crates/project_model/src/build_data.rs1
-rw-r--r--crates/project_model/src/cargo_workspace.rs101
-rw-r--r--crates/project_model/src/rustc_cfg.rs45
-rw-r--r--crates/project_model/src/workspace.rs46
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 {
119pub struct PackageDependency { 119pub 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)]
126pub 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
135impl 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
385fn 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
408fn 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
3use std::process::Command; 3use std::process::Command;
4 4
5use anyhow::Result;
6use paths::AbsPath;
7
5use crate::{cfg_flag::CfgFlag, utf8_stdout}; 8use crate::{cfg_flag::CfgFlag, utf8_stdout};
6 9
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { 10pub(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
30fn 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
7use anyhow::{Context, Result}; 7use anyhow::{Context, Result};
8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9use cargo_workspace::DepKind;
9use cfg::CfgOptions; 10use cfg::CfgOptions;
10use paths::{AbsPath, AbsPathBuf}; 11use paths::{AbsPath, AbsPathBuf};
11use proc_macro_api::ProcMacroClient; 12use 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,