diff --git a/Cargo.lock b/Cargo.lock index 1a480d6911fe..c40157cfa377 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3000,6 +3000,7 @@ name = "toolchain" version = "0.0.0" dependencies = [ "camino", + "semver", ] [[package]] diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index b5db24e98bc7..72befc58a123 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -30,6 +30,7 @@ fn current_machine_target_data() -> TargetData { QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), + None, ) .unwrap() } diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs index 9f84f632d5e4..8da0a574e764 100644 --- a/crates/project-model/src/build_dependencies.rs +++ b/crates/project-model/src/build_dependencies.rs @@ -462,9 +462,7 @@ impl WorkspaceBuildScripts { cmd.arg(target_dir.as_ref()); } - if let Some(target) = &config.target { - cmd.args(["--target", target]); - } + toolchain::cargo_use_targets(toolchain, &mut cmd, config.target.as_slice()); let mut lockfile_copy = None; if let Some(toolchain) = toolchain { let lockfile_path = diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 97375fe9ddd1..a0a9815d8756 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -704,9 +704,18 @@ impl FetchMetadata { } if !config.targets.is_empty() { - other_options.extend( - config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]), - ); + let mut has_json_target = false; + other_options.extend(config.targets.iter().flat_map(|target| { + has_json_target |= target.ends_with(".json"); + ["--filter-platform".to_owned(), target.clone()] + })); + if has_json_target + && config.toolchain_version.as_ref().is_some_and(|version| { + *version >= toolchain::MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG + }) + { + other_options.push("-Zjson-target-spec".to_owned()); + } } command.other_options(other_options.clone()); diff --git a/crates/project-model/src/toolchain_info/target_data.rs b/crates/project-model/src/toolchain_info/target_data.rs index b815c0b79718..18ceac6fc766 100644 --- a/crates/project-model/src/toolchain_info/target_data.rs +++ b/crates/project-model/src/toolchain_info/target_data.rs @@ -39,6 +39,7 @@ pub fn get( config: QueryConfig<'_>, target: Option<&str>, extra_env: &FxHashMap>, + version: Option<&semver::Version>, ) -> anyhow::Result { const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; let process = |output: String| { @@ -55,9 +56,7 @@ pub fn get( let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); - if let Some(target) = target { - cmd.args(["--target", target]); - } + toolchain::cargo_use_targets(version, &mut cmd, target.as_slice()); cmd.args(["--", "-Z", "unstable-options"]); match utf8_stdout(&mut cmd) { Ok(output) => return process(output), @@ -95,13 +94,17 @@ mod tests { let manifest_path = ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None); - assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + let extra_env = &FxHashMap::default(); + let Ok(version) = super::super::version::get(cfg, extra_env) else { return }; + assert!(get(cfg, None, extra_env, version.as_ref()).is_ok()); } #[test] fn rustc() { let sysroot = Sysroot::empty(); let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); - assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + let extra_env = &FxHashMap::default(); + let Ok(version) = super::super::version::get(cfg, extra_env) else { return }; + assert!(get(cfg, None, extra_env, version.as_ref()).is_ok()); } } diff --git a/crates/project-model/src/toolchain_info/version.rs b/crates/project-model/src/toolchain_info/version.rs index 357053d8e825..c97a68719834 100644 --- a/crates/project-model/src/toolchain_info/version.rs +++ b/crates/project-model/src/toolchain_info/version.rs @@ -7,7 +7,7 @@ use toolchain::Tool; use crate::{toolchain_info::QueryConfig, utf8_stdout}; -pub(crate) fn get( +pub fn get( config: QueryConfig<'_>, extra_env: &FxHashMap>, ) -> Result, anyhow::Error> { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 9002b0c3bbf1..9010ba882c29 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -316,13 +316,18 @@ impl ProjectWorkspace { let target_data = Builder::new() .name("ProjectWorkspace::target_data".to_owned()) .spawn_scoped(s, || { - target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env) - .inspect_err(|e| { - tracing::error!(%e, - "failed fetching data layout for \ - {cargo_toml:?} workspace" - ) - }) + target_data::get( + toolchain_config, + targets.first().map(Deref::deref), + extra_env, + toolchain.as_ref(), + ) + .inspect_err(|e| { + tracing::error!(%e, + "failed fetching data layout for \ + {cargo_toml:?} workspace" + ) + }) }) .expect("failed to spawn thread"); @@ -495,7 +500,12 @@ impl ProjectWorkspace { rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let data_layout = s.spawn(|| { - target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env) + target_data::get( + query_config, + targets.first().map(Deref::deref), + &config.extra_env, + toolchain.as_ref(), + ) }); let loaded_sysroot = s.spawn(|| { if let Some(sysroot_project) = sysroot_project { @@ -564,7 +574,8 @@ impl ProjectWorkspace { let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) .unwrap_or_default(); let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); - let target_data = target_data::get(query_config, None, &config.extra_env); + let target_data = + target_data::get(query_config, None, &config.extra_env, toolchain.as_ref()); let loaded_sysroot = sysroot.load_workspace( &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config( diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 69405b421eca..5225c49c130a 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -10,7 +10,7 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig}; use ide_db::base_db; use itertools::Either; use profile::StopWatch; -use project_model::toolchain_info::{QueryConfig, target_data}; +use project_model::toolchain_info::{QueryConfig, target_data, version}; use project_model::{ CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, RustSourceWorkspaceConfig, Sysroot, @@ -81,10 +81,13 @@ impl Tester { sysroot.set_workspace(loaded_sysroot); } + let query_config = QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()); + let toolchain_version = version::get(query_config, &cargo_config.extra_env).ok().flatten(); let target_data = target_data::get( - QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), + query_config, None, &cargo_config.extra_env, + toolchain_version.as_ref(), ); let workspace = ProjectWorkspace { diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index b927a1160415..99e640a3cecb 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -69,10 +69,9 @@ impl CargoOptions { cmd: &mut Command, ws_target_dir: Option<&Utf8Path>, package_repr: Option<&str>, + toolchain_version: Option<&semver::Version>, ) { - for target in &self.target_tuples { - cmd.args(["--target", target.as_str()]); - } + toolchain::cargo_use_targets(toolchain_version, cmd, &self.target_tuples); if self.all_targets { if self.set_test { cmd.arg("--all-targets"); @@ -227,6 +226,7 @@ impl FlycheckHandle { workspace_root: AbsPathBuf, manifest_path: Option, ws_target_dir: Option, + toolchain_version: Option, ) -> FlycheckHandle { let actor = FlycheckActor::new( id, @@ -238,6 +238,7 @@ impl FlycheckHandle { workspace_root, manifest_path, ws_target_dir, + toolchain_version, ); let (sender, receiver) = unbounded::(); let thread = @@ -445,6 +446,7 @@ struct FlycheckActor { command_receiver: Option>, diagnostics_cleared_for: FxHashSet, diagnostics_received: DiagnosticsReceived, + toolchain_version: Option, } #[derive(PartialEq, Debug)] @@ -531,6 +533,7 @@ impl FlycheckActor { workspace_root: AbsPathBuf, manifest_path: Option, ws_target_dir: Option, + toolchain_version: Option, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { @@ -548,6 +551,7 @@ impl FlycheckActor { command_receiver: None, diagnostics_cleared_for: Default::default(), diagnostics_received: DiagnosticsReceived::NotYet, + toolchain_version, } } @@ -958,6 +962,7 @@ impl FlycheckActor { &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), package_repr, + self.toolchain_version.as_ref(), ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 41e3db7f3191..ba3b8dbcd2f7 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -287,6 +287,7 @@ pub(crate) fn handle_run_test( Some(cargo.target_directory().as_ref()), target, state.test_run_sender.clone(), + ws.toolchain.as_ref(), )?; handles.push(handle); } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 3bf3cd562255..173409b32585 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -901,6 +901,7 @@ impl GlobalState { self.config.default_root_path().clone(), None, None, + None, )] } crate::flycheck::InvocationStrategy::PerWorkspace => { @@ -942,21 +943,30 @@ impl GlobalState { ProjectWorkspaceKind::DetachedFile { .. } => return None, }, ws.sysroot.root().map(ToOwned::to_owned), + ws.toolchain.clone(), )) }) - .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| { - FlycheckHandle::spawn( + .map( + |( id, - generation.clone(), - sender.clone(), - config.clone(), - config_json, + (config_json, root, manifest_path, target_dir), sysroot_root, - root.to_path_buf(), - manifest_path.map(|it| it.to_path_buf()), - target_dir.map(|it| AsRef::::as_ref(it).to_path_buf()), - ) - }) + toolchain, + )| { + FlycheckHandle::spawn( + id, + generation.clone(), + sender.clone(), + config.clone(), + config_json, + sysroot_root, + root.to_path_buf(), + manifest_path.map(|it| it.to_path_buf()), + target_dir.map(|it| AsRef::::as_ref(it).to_path_buf()), + toolchain, + ) + }, + ) .collect() } } diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs index c6f8a7c799c2..62d242f9d083 100644 --- a/crates/rust-analyzer/src/test_runner.rs +++ b/crates/rust-analyzer/src/test_runner.rs @@ -108,6 +108,7 @@ impl CargoTestHandle { ws_target_dir: Option<&Utf8Path>, test_target: TestTarget, sender: Sender, + toolchain_version: Option<&semver::Version>, ) -> anyhow::Result { let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); @@ -131,7 +132,12 @@ impl CargoTestHandle { cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); + options.apply_on_command( + &mut cmd, + ws_target_dir, + Some(&test_target.package), + toolchain_version, + ); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/crates/toolchain/Cargo.toml b/crates/toolchain/Cargo.toml index d0d5840e785a..8ba63ae86865 100644 --- a/crates/toolchain/Cargo.toml +++ b/crates/toolchain/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] camino.workspace = true +semver.workspace = true [lints] workspace = true diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs index ea08f7aacac4..6bed98f4cd2d 100644 --- a/crates/toolchain/src/lib.rs +++ b/crates/toolchain/src/lib.rs @@ -143,3 +143,33 @@ pub fn probe_for_binary(path: Utf8PathBuf) -> Option { }; iter::once(path).chain(with_extension).find(|it| it.is_file()) } + +pub const MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG: semver::Version = + semver::Version { + major: 1, + minor: 95, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + +/// Uses targets in a Cargo process. +pub fn cargo_use_targets( + toolchain_version: Option<&semver::Version>, + cmd: &mut Command, + targets: impl IntoIterator>, +) { + let mut has_json_target = false; + for target in targets { + let target = target.as_ref(); + cmd.args(["--target", target]); + has_json_target |= target.ends_with(".json"); + } + if has_json_target + && toolchain_version.is_some_and(|version| { + *version >= MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG + }) + { + cmd.arg("-Zjson-target-spec"); + } +}