Repository: nextest-rs/nextest (language: rust)

Issue:
Bug: default deduced target triple is wrong
### Description of the issue

**Description:**

You use `target_spec::Platform::current()` to deduce the host target triple in many locations ([e.g.](https://github.com/nextest-rs/nextest/blob/c872a3b8654cc1a8444d87589f969885c8ab0764/nextest-runner/src/platform.rs#L185)). This is wrong; `Platform::current()` is the triple used at *build time*, causing nextest to think, for example, that the host target triple is `x86_64-unknown-linux-musl` if you obtained a binary distribution of nextest that was built using musl. You should deduce the host target from the stdout of `rustc -vV` instead.

**Steps to reproduce:**
1. On a system with the `x86_64-unknown-linux-gnu` toolchain installed, obtain the binary distribution of nextest that is built with `x86_64-unknown-linux-musl`
2. Run `CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=foobar cargo nextest run`
4. Run `CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUNNER=foobar cargo nextest run`


### Expected outcome

When running `CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=foobar cargo nextest run`: nextest detects the runner.
When running `CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUNNER=foobar cargo nextest run`: nextest does not detect the runner.

### Actual result

When running `CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER=foobar cargo nextest run`: nextest does not detect the runner.
When running `CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_RUNNER=foobar cargo nextest run`: nextest erroneously detect the runner.


### Nextest version

```text
0.9.88
```

### Additional context

_No response_


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 2c4372414de..ffa8c6f0406 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -373,6 +373,7 @@ dependencies = [
  "duct",
  "enable-ansi-support",
  "guppy",
+ "indent_write",
  "itertools 0.14.0",
  "miette",
  "nextest-filtering",
@@ -872,16 +873,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
-[[package]]
-name = "erased-serde"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
-dependencies = [
- "serde",
- "typeid",
-]
-
 [[package]]
 name = "errno"
 version = "0.3.9"
@@ -945,9 +936,9 @@ dependencies = [
 
 [[package]]
 name = "fixedbitset"
-version = "0.4.2"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
 
 [[package]]
 name = "fixture-data"
@@ -1149,9 +1140,9 @@ dependencies = [
 
 [[package]]
 name = "guppy"
-version = "0.17.12"
+version = "0.17.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a70d0754232d1c2fd4819914085dc06cccbe2f2377905cdd2e668ca77df6e72"
+checksum = "54c43505e290c5dc9ca6fc7117927ae46685ae6ea23f988996d622a6394ca99b"
 dependencies = [
  "ahash",
  "camino",
@@ -1161,7 +1152,7 @@ dependencies = [
  "fixedbitset",
  "guppy-workspace-hack",
  "indexmap 2.7.1",
- "itertools 0.13.0",
+ "itertools 0.14.0",
  "nested",
  "once_cell",
  "pathdiff",
@@ -1668,12 +1659,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.24"
+version = "0.4.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6ea2a48c204030ee31a7d7fc72c93294c92fe87ecb1789881c9543516e1a0d"
-dependencies = [
- "value-bag",
-]
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
 
 [[package]]
 name = "maplit"
@@ -2045,9 +2033,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.2"
+version = "1.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
 
 [[package]]
 name = "openssl"
@@ -2155,9 +2143,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "petgraph"
-version = "0.6.5"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
 dependencies = [
  "fixedbitset",
  "indexmap 2.7.1",
@@ -2651,9 +2639,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.18"
+version = "1.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
 
 [[package]]
 name = "same-file"
@@ -2765,15 +2753,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "serde_fmt"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4"
-dependencies = [
- "serde",
-]
-
 [[package]]
 name = "serde_ignored"
 version = "0.1.10"
@@ -3037,84 +3016,6 @@ version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2"
 
-[[package]]
-name = "sval"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8"
-
-[[package]]
-name = "sval_buffer"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f"
-dependencies = [
- "sval",
- "sval_ref",
-]
-
-[[package]]
-name = "sval_dynamic"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4"
-dependencies = [
- "sval",
-]
-
-[[package]]
-name = "sval_fmt"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94"
-dependencies = [
- "itoa",
- "ryu",
- "sval",
-]
-
-[[package]]
-name = "sval_json"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155"
-dependencies = [
- "itoa",
- "ryu",
- "sval",
-]
-
-[[package]]
-name = "sval_nested"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f"
-dependencies = [
- "sval",
- "sval_buffer",
- "sval_ref",
-]
-
-[[package]]
-name = "sval_ref"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa"
-dependencies = [
- "sval",
-]
-
-[[package]]
-name = "sval_serde"
-version = "2.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a"
-dependencies = [
- "serde",
- "sval",
- "sval_nested",
-]
-
 [[package]]
 name = "swrite"
 version = "0.1.0"
@@ -3123,9 +3024,9 @@ checksum = "7f3fece30b2dc06d65ecbca97b602db15bf75f932711d60cc604534f1f8b7a03"
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3166,9 +3067,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
 [[package]]
 name = "target-spec"
-version = "3.3.1"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da00bc76fdc8b7ed7163b7cf728179277b26985c177510b69cc320f30b5a517f"
+checksum = "d9e2827c8368f9860f6d263f872175b7dd1d62628fc7518335fb1a660e477d6a"
 dependencies = [
  "cfg-expr",
  "guppy-workspace-hack",
@@ -3593,12 +3494,6 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
-[[package]]
-name = "typeid"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
-
 [[package]]
 name = "typenum"
 version = "1.17.0"
@@ -3700,42 +3595,6 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 
-[[package]]
-name = "value-bag"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
-dependencies = [
- "value-bag-serde1",
- "value-bag-sval2",
-]
-
-[[package]]
-name = "value-bag-serde1"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b"
-dependencies = [
- "erased-serde",
- "serde",
- "serde_fmt",
-]
-
-[[package]]
-name = "value-bag-sval2"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a"
-dependencies = [
- "sval",
- "sval_buffer",
- "sval_dynamic",
- "sval_fmt",
- "sval_json",
- "sval_ref",
- "sval_serde",
-]
-
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
diff --git a/Cargo.toml b/Cargo.toml
index 8e07550909a..844ecfb39e5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -61,7 +61,7 @@ fs-err = "3.1.0"
 future-queue = "0.3.0"
 futures = "0.3.31"
 globset = "0.4.15"
-guppy = "0.17.12"
+guppy = "0.17.13"
 hex = "0.4.3"
 home = "0.5.11"
 http = "1.2.0"
@@ -76,7 +76,7 @@ is_ci = "1.2.0"
 itertools = "0.14.0"
 libc = "0.2.169"
 libtest-mimic = "0.8.1"
-log = "0.4.24"
+log = "0.4.25"
 maplit = "1.0.2"
 miette = "7.5.0"
 mukti-metadata = "0.3.0"
@@ -116,7 +116,7 @@ supports-color = "3.0.2"
 supports-unicode = "3.0.0"
 swrite = "0.1.0"
 tar = "0.4.43"
-target-spec = { version = "3.3.1", features = ["custom", "summaries"] }
+target-spec = { version = "3.4.0", features = ["custom", "summaries"] }
 target-spec-miette = "0.4.4"
 test-case = "3.3.1"
 test-strategy = "0.4.0"
diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml
index 5e0f0b15996..5c93155daa2 100644
--- a/workspace-hack/Cargo.toml
+++ b/workspace-hack/Cargo.toml
@@ -26,7 +26,7 @@ console = { version = "0.15.8" }
 either = { version = "1.13.0" }
 getrandom = { version = "0.2.15", default-features = false, features = ["std"] }
 indexmap = { version = "2.7.1", features = ["serde"] }
-log = { version = "0.4.24", default-features = false, features = ["std"] }
+log = { version = "0.4.25", default-features = false, features = ["std"] }
 memchr = { version = "2.7.4" }
 miette = { version = "7.5.0", features = ["fancy"] }
 num-traits = { version = "0.2.19", default-features = false, features = ["std"] }
@@ -36,7 +36,7 @@ regex-syntax = { version = "0.8.5" }
 serde = { version = "1.0.217", features = ["alloc", "derive"] }
 serde_json = { version = "1.0.138", features = ["unbounded_depth"] }
 smallvec = { version = "1.13.2", default-features = false, features = ["const_generics"] }
-target-spec = { version = "3.3.1", default-features = false, features = ["custom", "summaries"] }
+target-spec = { version = "3.4.0", default-features = false, features = ["custom", "summaries"] }
 target-spec-miette = { version = "0.4.4", default-features = false, features = ["fixtures"] }
 tokio = { version = "1.43.0", features = ["fs", "io-std", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time", "tracing"] }
 tracing-core = { version = "0.1.33" }
@@ -50,7 +50,7 @@ cc = { version = "1.2.5", default-features = false, features = ["parallel"] }
 proc-macro2 = { version = "1.0.93" }
 quote = { version = "1.0.38" }
 serde = { version = "1.0.217", features = ["alloc", "derive"] }
-syn = { version = "2.0.96", features = ["extra-traits", "full", "visit", "visit-mut"] }
+syn = { version = "2.0.98", features = ["extra-traits", "full", "visit", "visit-mut"] }
 
 [target.x86_64-unknown-linux-gnu.dependencies]
 futures-channel = { version = "0.3.31", features = ["sink"] }


Test patch:
diff --git a/cargo-nextest/Cargo.toml b/cargo-nextest/Cargo.toml
index 0842abf4aef..efbe3f0aa21 100644
--- a/cargo-nextest/Cargo.toml
+++ b/cargo-nextest/Cargo.toml
@@ -20,6 +20,7 @@ dialoguer.workspace = true
 duct.workspace = true
 enable-ansi-support.workspace = true
 guppy.workspace = true
+indent_write.workspace = true
 itertools.workspace = true
 miette = { workspace = true, features = ["fancy"] }
 # Instead of using workspace dependencies which have floating versions, we pin exact versions here
diff --git a/cargo-nextest/src/dispatch.rs b/cargo-nextest/src/dispatch.rs
index 9afba3e467b..8628261d96c 100644
--- a/cargo-nextest/src/dispatch.rs
+++ b/cargo-nextest/src/dispatch.rs
@@ -1155,22 +1155,7 @@ impl BaseApp {
         // Next, read the build platforms.
         let build_platforms = match reuse_build.binaries_metadata() {
             Some(kind) => kind.binary_list.rust_build_meta.build_platforms.clone(),
-            None => {
-                let host = HostPlatform::current(PlatformLibdir::from_rustc_stdout(
-                    RustcCli::print_host_libdir().read(),
-                ))?;
-
-                let triple_info =
-                    discover_target_triple(&cargo_configs, cargo_opts.target.as_deref())?;
-                let target = triple_info.map(|triple| {
-                    let libdir = PlatformLibdir::from_rustc_stdout(
-                        RustcCli::print_target_libdir(&triple).read(),
-                    );
-                    TargetPlatform::new(triple, libdir)
-                });
-
-                BuildPlatforms { host, target }
-            }
+            None => detect_build_platforms(&cargo_configs, cargo_opts.target.as_deref())?,
         };
 
         // Read the Cargo metadata.
@@ -2103,8 +2088,8 @@ enum DebugCommand {
     /// Print the current executable path.
     CurrentExe,
 
-    /// Show the target platform that nextest would use.
-    ShowTarget {
+    /// Show the build platforms that nextest would use.
+    BuildPlatforms {
         /// The target triple to use.
         #[arg(long)]
         target: Option<String>,
@@ -2112,6 +2097,10 @@ enum DebugCommand {
         /// Override a Cargo configuration value.
         #[arg(long, value_name = "KEY=VALUE")]
         config: Vec<String>,
+
+        /// Output format.
+        #[arg(long, value_enum, default_value_t)]
+        output_format: BuildPlatformsOutputFormat,
     },
 }
 
@@ -2173,13 +2162,31 @@ impl DebugCommand {
                     .map_err(|err| ExpectedError::GetCurrentExeFailed { err })?;
                 println!("{}", exe.display());
             }
-            DebugCommand::ShowTarget { target, config } => {
+            DebugCommand::BuildPlatforms {
+                target,
+                config,
+                output_format,
+            } => {
                 let cargo_configs = CargoConfigs::new(&config).map_err(Box::new)?;
-                let target = discover_target_triple(&cargo_configs, target.as_deref())?;
-                if let Some(target) = target {
-                    println!("{:#?}", target);
-                } else {
-                    println!("no target triple found");
+                let build_platforms = detect_build_platforms(&cargo_configs, target.as_deref())?;
+                match output_format {
+                    BuildPlatformsOutputFormat::Debug => {
+                        println!("{:#?}", build_platforms);
+                    }
+                    BuildPlatformsOutputFormat::Triple => {
+                        println!(
+                            "host triple: {}",
+                            build_platforms.host.platform.triple().as_str()
+                        );
+                        if let Some(target) = &build_platforms.target {
+                            println!(
+                                "target triple: {}",
+                                target.triple.platform.triple().as_str()
+                            );
+                        } else {
+                            println!("target triple: (none)");
+                        }
+                    }
                 }
             }
         }
@@ -2262,6 +2269,17 @@ impl fmt::Display for ExtractOutputFormat {
     }
 }
 
+/// Output format for `nextest debug build-platforms`.
+#[derive(Clone, Copy, Debug, Default, ValueEnum)]
+pub enum BuildPlatformsOutputFormat {
+    /// Show Debug output.
+    #[default]
+    Debug,
+
+    /// Show just the triple.
+    Triple,
+}
+
 fn acquire_graph_data(
     manifest_path: Option<&Utf8Path>,
     target_dir: Option<&Utf8Path>,
@@ -2303,6 +2321,22 @@ fn acquire_graph_data(
     Ok(json)
 }
 
+fn detect_build_platforms(
+    cargo_configs: &CargoConfigs,
+    target_cli_option: Option<&str>,
+) -> Result<BuildPlatforms, ExpectedError> {
+    let host = HostPlatform::detect(PlatformLibdir::from_rustc_stdout(
+        RustcCli::print_host_libdir().read(),
+    ))?;
+    let triple_info = discover_target_triple(cargo_configs, target_cli_option)?;
+    let target = triple_info.map(|triple| {
+        let libdir =
+            PlatformLibdir::from_rustc_stdout(RustcCli::print_target_libdir(&triple).read());
+        TargetPlatform::new(triple, libdir)
+    });
+    Ok(BuildPlatforms { host, target })
+}
+
 fn discover_target_triple(
     cargo_configs: &CargoConfigs,
     target_cli_option: Option<&str>,
diff --git a/cargo-nextest/src/errors.rs b/cargo-nextest/src/errors.rs
index fc1372f0c69..2e39c3b9d1a 100644
--- a/cargo-nextest/src/errors.rs
+++ b/cargo-nextest/src/errors.rs
@@ -3,6 +3,7 @@
 
 use crate::{output::StderrStyles, ExtractOutputFormat};
 use camino::Utf8PathBuf;
+use indent_write::indentable::Indented;
 use itertools::Itertools;
 use nextest_filtering::errors::FiltersetParseErrors;
 use nextest_metadata::NextestExitCode;
@@ -90,9 +91,9 @@ pub enum ExpectedError {
         err: TestFilterBuilderError,
     },
     #[error("unknown host platform")]
-    UnknownHostPlatform {
+    HostPlatformDetectError {
         #[from]
-        err: UnknownHostPlatform,
+        err: HostPlatformDetectError,
     },
     #[error("target triple error")]
     TargetTripleError {
@@ -398,7 +399,7 @@ impl ExpectedError {
             | Self::RootManifestNotFound { .. }
             | Self::CargoConfigError { .. }
             | Self::TestFilterBuilderError { .. }
-            | Self::UnknownHostPlatform { .. }
+            | Self::HostPlatformDetectError { .. }
             | Self::TargetTripleError { .. }
             | Self::MetadataMaterializeError { .. }
             | Self::UnknownArchiveFormat { .. }
@@ -652,8 +653,8 @@ impl ExpectedError {
                 error!("{err}");
                 err.source()
             }
-            Self::UnknownHostPlatform { err } => {
-                error!("the host platform was unknown to nextest");
+            Self::HostPlatformDetectError { err } => {
+                error!("the host platform could not be detected");
                 Some(err as &dyn Error)
             }
             Self::TargetTripleError { err } => {
@@ -882,7 +883,11 @@ impl ExpectedError {
         };
 
         while let Some(err) = next_error {
-            error!(target: "cargo_nextest::no_heading", "\nCaused by:\n  {}", err);
+            error!(
+                target: "cargo_nextest::no_heading",
+                "\nCaused by:\n{}",
+                Indented { item: err, indent: "  " },
+            );
             next_error = err.source();
         }
     }
diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml
index 90f94af4d86..a0299db871b 100644
--- a/integration-tests/Cargo.toml
+++ b/integration-tests/Cargo.toml
@@ -13,6 +13,10 @@ path = "test-helpers/cargo-nextest-dup.rs"
 name = "build-seed-archive"
 path = "test-helpers/build-seed-archive.rs"
 
+[[bin]]
+name = "rustc-shim"
+path = "test-helpers/rustc-shim.rs"
+
 [[test]]
 name = "custom-harness"
 harness = false
diff --git a/integration-tests/src/nextest_cli.rs b/integration-tests/src/nextest_cli.rs
index a4d448cca4a..a9ca2a4cc50 100644
--- a/integration-tests/src/nextest_cli.rs
+++ b/integration-tests/src/nextest_cli.rs
@@ -153,6 +153,24 @@ impl CargoNextestOutput {
     pub fn decode_test_list_json(&self) -> Result<TestListSummary> {
         Ok(serde_json::from_slice(&self.stdout)?)
     }
+
+    /// Returns the output as a (hopefully) platform-independent snapshot that
+    /// can be checked in and compared.
+    pub fn to_snapshot(&self) -> String {
+        // Don't include the command as its representation is
+        // platform-dependent.
+        let output = format!(
+            "exit code: {:?}\n\
+            --- stdout ---\n{}\n\n--- stderr ---\n{}\n",
+            self.exit_status.code(),
+            String::from_utf8_lossy(&self.stdout),
+            String::from_utf8_lossy(&self.stderr),
+        );
+
+        // Turn "exit status" and "exit code" into "exit status|code"
+        let output = output.replace("exit status: ", "exit status|code: ");
+        output.replace("exit code: ", "exit status|code: ")
+    }
 }
 
 impl fmt::Display for CargoNextestOutput {
diff --git a/integration-tests/test-helpers/rustc-shim.rs b/integration-tests/test-helpers/rustc-shim.rs
new file mode 100644
index 00000000000..e53550fd6c5
--- /dev/null
+++ b/integration-tests/test-helpers/rustc-shim.rs
@@ -0,0 +1,82 @@
+// Copyright (c) The nextest Contributors
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+//! A shim for rustc that is used for injecting errors.
+
+use std::{
+    env::args,
+    process::{Command, ExitCode},
+};
+
+fn main() -> ExitCode {
+    // Currently, `--version --verbose` is supported. (This is a bit
+    // overdetermined, but it's okay.)
+    let args = args().collect::<Vec<_>>();
+
+    if &args[1] == "--version" && &args[2] == "--verbose" {
+        match version_verbose_error() {
+            Some(VersionVerboseError::NonZeroExitCode) => {
+                println!("failure output to stdout");
+                eprintln!("failure output to stderr");
+                return ExitCode::FAILURE;
+            }
+            Some(VersionVerboseError::InvalidStdout) => {
+                println!("invalid output to stdout");
+                eprintln!("invalid output to stderr");
+                return ExitCode::SUCCESS;
+            }
+            Some(VersionVerboseError::InvalidTriple) => {
+                println!(
+                    "rustc 1.84.1 (e71f9a9a9 2025-01-27)\n\
+                     binary: rustc\n\
+                     commit-hash: e71f9a9a98b0faf423844bf0ba7438f29dc27d58\n\
+                     commit-date: 2025-01-27\n\
+                     host: invalid-triple\n\
+                     release: 1.84.1\n\
+                     LLVM version: 19.1.5\n\
+                    "
+                );
+                return ExitCode::SUCCESS;
+            }
+            None => {
+                // Pass through to the real rustc.
+            }
+        }
+    }
+
+    let code = Command::new(rustc_path())
+        .args(&args[1..])
+        .status()
+        .expect("rustc executed successfully")
+        .code();
+
+    std::process::exit(code.unwrap_or(1))
+}
+
+#[derive(Clone, Copy, Debug)]
+enum VersionVerboseError {
+    NonZeroExitCode,
+    InvalidStdout,
+    InvalidTriple,
+}
+
+fn version_verbose_error() -> Option<VersionVerboseError> {
+    const VAR: &str = "__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR";
+    match std::env::var(VAR) {
+        Ok(s) => match s.as_str() {
+            "non-zero" => Some(VersionVerboseError::NonZeroExitCode),
+            "invalid-stdout" => Some(VersionVerboseError::InvalidStdout),
+            "invalid-triple" => Some(VersionVerboseError::InvalidTriple),
+            _ => panic!("unrecognized value for {}: {}", VAR, s),
+        },
+        Err(_) => None,
+    }
+}
+
+fn rustc_path() -> String {
+    const VAR: &str = "__NEXTEST_RUSTC_SHIM_RUSTC";
+    match std::env::var(VAR) {
+        Ok(s) => s,
+        Err(_) => panic!("{VAR} not set"),
+    }
+}
diff --git a/integration-tests/tests/datatest/custom_target.rs b/integration-tests/tests/datatest/custom_target.rs
index 4106850dbe6..31078af31c2 100644
--- a/integration-tests/tests/datatest/custom_target.rs
+++ b/integration-tests/tests/datatest/custom_target.rs
@@ -24,7 +24,7 @@ pub(crate) fn custom_invalid(path: &Utf8Path, contents: String) -> datatest_stab
             "--color",
             "always",
             "debug",
-            "show-target",
+            "build-platforms",
             "--target",
             json_path.as_str(),
         ])
diff --git a/integration-tests/tests/integration/main.rs b/integration-tests/tests/integration/main.rs
index 25ef476ef1d..3660d39dff0 100644
--- a/integration-tests/tests/integration/main.rs
+++ b/integration-tests/tests/integration/main.rs
@@ -29,7 +29,7 @@ use integration_tests::{
 };
 use nextest_metadata::{BuildPlatform, NextestExitCode, TestListSummary};
 use std::{borrow::Cow, fs::File, io::Write};
-use target_spec::Platform;
+use target_spec::{summaries::TargetFeaturesSummary, Platform};
 
 mod fixtures;
 mod stuck_signal;
@@ -1382,8 +1382,9 @@ fn test_target_arg() {
     set_env_vars();
     let p = TempProject::new().unwrap();
 
-    let host_platform = Platform::current().expect("should detect the host target successfully");
-    let host_triple = host_platform.triple_str();
+    let build_target_platform =
+        Platform::build_target().expect("should detect the host target successfully");
+    let host_triple = build_target_platform.triple_str();
     let output = CargoNextestCli::for_test()
         .args([
             "--manifest-path",
@@ -1400,10 +1401,110 @@ fn test_target_arg() {
         .rust_build_meta
         .platforms
         .expect("should have the platforms field");
-    assert_eq!(build_platforms.host.platform, host_platform.to_summary());
+
+    // Target features get reset to unknown, unfortunately, so we can't compare
+    // the full platform.
+    let mut summary = build_target_platform.to_summary();
+    summary.target_features = TargetFeaturesSummary::Unknown;
+    assert_eq!(build_platforms.host.platform, summary);
+
     assert_eq!(build_platforms.targets[0].platform.triple, host_triple);
     assert_eq!(
         build_platforms.targets[0].libdir,
         build_platforms.host.libdir
     );
 }
+
+#[test]
+fn test_rustc_version_verbose_errors() {
+    set_env_vars();
+
+    // Set RUSTC to the shim.
+    let shim_rustc = std::env::var("NEXTEST_BIN_EXE_rustc-shim").unwrap();
+
+    let mut command = CargoNextestCli::for_test();
+    command
+        .args(["debug", "build-platforms", "--output-format", "triple"])
+        .env("RUSTC", &shim_rustc);
+
+    // --- Error cases ---
+    {
+        let mut command = command.clone();
+        command
+            .env("__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR", "non-zero")
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "error");
+        insta::assert_snapshot!(
+            "rustc_vv_non_zero",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+
+    {
+        let mut command = command.clone();
+        command
+            .env(
+                "__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR",
+                "invalid-stdout",
+            )
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "error");
+        insta::assert_snapshot!(
+            "rustc_vv_invalid_stdout",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+
+    {
+        let mut command = command.clone();
+        command
+            .env(
+                "__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR",
+                "invalid-triple",
+            )
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "error");
+
+        insta::assert_snapshot!(
+            "rustc_vv_invalid_triple",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+
+    // --- Warning cases ---
+    {
+        let mut command = command.clone();
+        command
+            .env("__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR", "non-zero")
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "x86_64-unknown-linux-gnu");
+        insta::assert_snapshot!(
+            "rustc_vv_non_zero_warning",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+
+    {
+        let mut command = command.clone();
+        command
+            .env(
+                "__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR",
+                "invalid-stdout",
+            )
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "x86_64-unknown-linux-gnu");
+        insta::assert_snapshot!(
+            "rustc_vv_invalid_stdout_warning",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+
+    {
+        let mut command = command.clone();
+        command
+            .env(
+                "__NEXTEST_RUSTC_SHIM_VERSION_VERBOSE_ERROR",
+                "invalid-triple",
+            )
+            .env("__NEXTEST_FORCE_BUILD_TARGET", "x86_64-unknown-linux-gnu");
+        insta::assert_snapshot!(
+            "rustc_vv_invalid_triple_warning",
+            command.unchecked(true).output().to_snapshot()
+        );
+    }
+}
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout.snap
new file mode 100644
index 00000000000..8f9e591dff3
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout.snap
@@ -0,0 +1,28 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(96)
+--- stdout ---
+
+
+--- stderr ---
+error: the host platform could not be detected
+
+Caused by:
+  parsing `rustc -vV` output failed, and detecting the build target failed as well
+  - host platform error:
+    error parsing `rustc -vV` output
+      caused by:
+      - output from `rustc -vV` did not contain a `host: ` line; output:
+        ---
+        invalid output to stdout
+        ---
+  - build target error:
+    error parsing `rustc -vV` output
+      caused by:
+      - output from `rustc -vV` did not contain a `host: ` line; output:
+        ---
+        (__NEXTEST_FORCE_BUILD_TARGET set to "error", forcibly failing build target detection)
+        ---
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout_warning.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout_warning.snap
new file mode 100644
index 00000000000..073e5e071c9
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_stdout_warning.snap
@@ -0,0 +1,20 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(0)
+--- stdout ---
+host triple: x86_64-unknown-linux-gnu
+target triple: (none)
+
+
+--- stderr ---
+warning: for host platform, parsing `rustc -vV` failed; falling back to build target `x86_64-unknown-linux-gnu`
+- host platform error:
+  error parsing `rustc -vV` output
+    caused by:
+    - output from `rustc -vV` did not contain a `host: ` line; output:
+      ---
+      invalid output to stdout
+      ---
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple.snap
new file mode 100644
index 00000000000..de75aeb40b7
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple.snap
@@ -0,0 +1,27 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(96)
+--- stdout ---
+
+
+--- stderr ---
+error: the host platform could not be detected
+
+Caused by:
+  parsing `rustc -vV` output failed, and detecting the build target failed as well
+  - host platform error:
+    unknown platform triple
+      caused by:
+      - unknown triple string: invalid-triple
+      - triple not in builtin platforms and heuristic parsing failed
+      - Unrecognized architecture: invalid
+  - build target error:
+    error parsing `rustc -vV` output
+      caused by:
+      - output from `rustc -vV` did not contain a `host: ` line; output:
+        ---
+        (__NEXTEST_FORCE_BUILD_TARGET set to "error", forcibly failing build target detection)
+        ---
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple_warning.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple_warning.snap
new file mode 100644
index 00000000000..20c5245a038
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_invalid_triple_warning.snap
@@ -0,0 +1,19 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(0)
+--- stdout ---
+host triple: x86_64-unknown-linux-gnu
+target triple: (none)
+
+
+--- stderr ---
+warning: for host platform, parsing `rustc -vV` failed; falling back to build target `x86_64-unknown-linux-gnu`
+- host platform error:
+  unknown platform triple
+    caused by:
+    - unknown triple string: invalid-triple
+    - triple not in builtin platforms and heuristic parsing failed
+    - Unrecognized architecture: invalid
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero.snap
new file mode 100644
index 00000000000..86b27f9b240
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero.snap
@@ -0,0 +1,27 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(96)
+--- stdout ---
+
+
+--- stderr ---
+error: the host platform could not be detected
+
+Caused by:
+  `rustc -vV` failed with exit status|code: 1, and detecting the build target failed as well
+  - `rustc -vV` stdout:
+    failure output to stdout
+
+  - `rustc -vV` stderr:
+    failure output to stderr
+
+  - build target error:
+    error parsing `rustc -vV` output
+      caused by:
+      - output from `rustc -vV` did not contain a `host: ` line; output:
+        ---
+        (__NEXTEST_FORCE_BUILD_TARGET set to "error", forcibly failing build target detection)
+        ---
diff --git a/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero_warning.snap b/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero_warning.snap
new file mode 100644
index 00000000000..2d58e99b904
--- /dev/null
+++ b/integration-tests/tests/integration/snapshots/integration__rustc_vv_non_zero_warning.snap
@@ -0,0 +1,18 @@
+---
+source: integration-tests/tests/integration/main.rs
+expression: command.unchecked(true).output().to_snapshot()
+snapshot_kind: text
+---
+exit status|code: Some(0)
+--- stdout ---
+host triple: x86_64-unknown-linux-gnu
+target triple: (none)
+
+
+--- stderr ---
+warning: for host platform, `rustc -vV` failed with exit status|code: 1; falling back to build target `x86_64-unknown-linux-gnu`
+- `rustc -vV` stdout:
+  failure output to stdout
+
+- `rustc -vV` stderr:
+  failure output to stderr
diff --git a/nextest-runner/src/errors.rs b/nextest-runner/src/errors.rs
index 93000ae1db0..6c477c139d0 100644
--- a/nextest-runner/src/errors.rs
+++ b/nextest-runner/src/errors.rs
@@ -13,7 +13,7 @@ use crate::{
 };
 use camino::{FromPathBufError, Utf8Path, Utf8PathBuf};
 use config::ConfigError;
-use indent_write::fmt::IndentWriter;
+use indent_write::{fmt::IndentWriter, indentable::Indented};
 use itertools::{Either, Itertools};
 use nextest_filtering::errors::FiltersetParseErrors;
 use nextest_metadata::RustBinaryId;
@@ -385,7 +385,7 @@ impl<T: std::error::Error> fmt::Display for ErrorList<T> {
         )?;
         for error in &self.inner {
             let mut indent = IndentWriter::new_skip_initial("  ", f);
-            writeln!(indent, "* {}", DisplayErrorChain(error))?;
+            writeln!(indent, "* {}", DisplayErrorChain::new(error))?;
             f = indent.into_inner();
         }
         Ok(())
@@ -408,11 +408,24 @@ impl<T: std::error::Error> std::error::Error for ErrorList<T> {
 ///
 /// This is similar to the display-error-chain crate, but uses IndentWriter
 /// internally to ensure that subsequent lines are also nested.
-pub(crate) struct DisplayErrorChain<E>(E);
+pub(crate) struct DisplayErrorChain<E> {
+    error: E,
+    initial_indent: &'static str,
+}
 
 impl<E: std::error::Error> DisplayErrorChain<E> {
     pub(crate) fn new(error: E) -> Self {
-        Self(error)
+        Self {
+            error,
+            initial_indent: "",
+        }
+    }
+
+    pub(crate) fn new_with_initial_indent(initial_indent: &'static str, error: E) -> Self {
+        Self {
+            error,
+            initial_indent,
+        }
     }
 }
 
@@ -421,23 +434,26 @@ where
     E: std::error::Error,
 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.0)?;
+        let mut writer = IndentWriter::new(self.initial_indent, f);
+        write!(writer, "{}", self.error)?;
 
-        let Some(mut cause) = self.0.source() else {
+        let Some(mut cause) = self.error.source() else {
             return Ok(());
         };
 
-        write!(f, "\n  caused by:")?;
+        write!(writer, "\n  caused by:")?;
 
-        let mut indent = IndentWriter::new_skip_initial("  ", f);
         loop {
-            write!(indent, "\n- {}", cause)?;
+            writeln!(writer)?;
+            let mut indent = IndentWriter::new_skip_initial("    ", writer);
+            write!(indent, "  - {}", cause)?;
 
             let Some(next_cause) = cause.source() else {
                 break Ok(());
             };
 
             cause = next_cause;
+            writer = indent.into_inner();
         }
     }
 }
@@ -762,7 +778,7 @@ pub enum RustBuildMetaParseError {
 
     /// The host platform could not be determined.
     #[error("the host platform could not be determined")]
-    UnknownHostPlatform(#[source] target_spec::Error),
+    DetectBuildTargetError(#[source] target_spec::Error),
 
     /// The build metadata includes features unsupported.
     #[error("unsupported features in the build metadata: {message}")]
@@ -1459,12 +1475,80 @@ pub enum InvalidCargoCliConfigReason {
     DoesntProvideValue,
 }
 
-/// The host platform could not be determined.
+/// The host platform couldn't be detected.
 #[derive(Debug, Error)]
-#[error("the host platform could not be determined")]
-pub struct UnknownHostPlatform {
-    #[source]
-    pub(crate) error: target_spec::Error,
+pub enum HostPlatformDetectError {
+    /// Spawning `rustc -vV` failed, and detecting the build target failed as
+    /// well.
+    #[error(
+        "error spawning `rustc -vV`, and detecting the build \
+         target failed as well\n\
+         - rustc spawn error: {}\n\
+         - build target error: {}\n",
+        DisplayErrorChain::new_with_initial_indent("  ", error),
+        DisplayErrorChain::new_with_initial_indent("  ", build_target_error)
+    )]
+    RustcVvSpawnError {
+        /// The error.
+        error: std::io::Error,
+
+        /// The error that occurred while detecting the build target.
+        build_target_error: Box<target_spec::Error>,
+    },
+
+    /// `rustc -vV` exited with a non-zero code, and detecting the build target
+    /// failed as well.
+    #[error(
+        "`rustc -vV` failed with {}, and detecting the \
+         build target failed as well\n\
+         - `rustc -vV` stdout:\n{}\n\
+         - `rustc -vV` stderr:\n{}\n\
+         - build target error:\n{}\n",
+        status,
+        Indented { item: String::from_utf8_lossy(stdout), indent: "  " },
+        Indented { item: String::from_utf8_lossy(stderr), indent: "  " },
+        DisplayErrorChain::new_with_initial_indent("  ", build_target_error)
+    )]
+    RustcVvFailed {
+        /// The status.
+        status: ExitStatus,
+
+        /// The standard output from `rustc -vV`.
+        stdout: Vec<u8>,
+
+        /// The standard error from `rustc -vV`.
+        stderr: Vec<u8>,
+
+        /// The error that occurred while detecting the build target.
+        build_target_error: Box<target_spec::Error>,
+    },
+
+    /// Parsing the host platform failed, and detecting the build target failed
+    /// as well.
+    #[error(
+        "parsing `rustc -vV` output failed, and detecting the build target \
+         failed as well\n\
+         - host platform error:\n{}\n\
+         - build target error:\n{}\n",
+        DisplayErrorChain::new_with_initial_indent("  ", host_platform_error),
+        DisplayErrorChain::new_with_initial_indent("  ", build_target_error)
+    )]
+    HostPlatformParseError {
+        /// The error that occurred while parsing the host platform.
+        host_platform_error: Box<target_spec::Error>,
+
+        /// The error that occurred while detecting the build target.
+        build_target_error: Box<target_spec::Error>,
+    },
+
+    /// Test-only code: `rustc -vV` was not queried, and detecting the build
+    /// target failed as well.
+    #[error("test-only code, so `rustc -vV` was not called; failed to detect build target")]
+    BuildTargetError {
+        /// The error that occurred while detecting the build target.
+        #[source]
+        build_target_error: Box<target_spec::Error>,
+    },
 }
 
 /// An error occurred while determining the cross-compiling target triple.
diff --git a/nextest-runner/src/list/rust_build_meta.rs b/nextest-runner/src/list/rust_build_meta.rs
index 9cdce9df0bf..b7ed426b524 100644
--- a/nextest-runner/src/list/rust_build_meta.rs
+++ b/nextest-runner/src/list/rust_build_meta.rs
@@ -215,7 +215,7 @@ mod tests {
         BuildPlatformsSummary, HostPlatformSummary, PlatformLibdirSummary,
         PlatformLibdirUnavailable,
     };
-    use target_spec::summaries::PlatformSummary;
+    use target_spec::{summaries::PlatformSummary, Platform};
     use test_case::test_case;
 
     impl Default for RustBuildMeta<BinaryListState> {
@@ -235,15 +235,19 @@ mod tests {
     }
 
     fn host_current() -> HostPlatform {
-        HostPlatform::current(PlatformLibdir::Unavailable(
-            PlatformLibdirUnavailable::OLD_SUMMARY,
-        ))
-        .expect("should detect the host platform successfully")
+        HostPlatform {
+            platform: Platform::build_target()
+                .expect("should detect the build target successfully"),
+            libdir: PlatformLibdir::Unavailable(PlatformLibdirUnavailable::OLD_SUMMARY),
+        }
     }
 
     fn host_current_with_libdir(libdir: &str) -> HostPlatform {
-        HostPlatform::current(PlatformLibdir::Available(libdir.into()))
-            .expect("should detect the host platform successfully")
+        HostPlatform {
+            platform: Platform::build_target()
+                .expect("should detect the build target successfully"),
+            libdir: PlatformLibdir::Available(libdir.into()),
+        }
     }
 
     fn host_not_current_with_libdir(libdir: &str) -> HostPlatform {
@@ -427,8 +431,7 @@ mod tests {
 
         let rust_build_meta = RustBuildMeta {
             build_platforms: BuildPlatforms {
-                host: HostPlatform::current(PlatformLibdir::Available(host_libdir.clone()))
-                    .expect("should detect the host platform successfully"),
+                host: host_current_with_libdir(host_libdir.as_ref()),
                 target: Some(TargetPlatform::new(
                     TargetTriple::x86_64_unknown_linux_gnu(),
                     PlatformLibdir::Available(target_libdir.clone()),
@@ -467,8 +470,7 @@ mod tests {
             linked_paths: [(Utf8PathBuf::from(tmpdir_dirname), Default::default())].into(),
             base_output_directories: [Utf8PathBuf::from(tmpdir_dirname)].into(),
             build_platforms: BuildPlatforms {
-                host: HostPlatform::current(PlatformLibdir::Available(host_libdir.clone()))
-                    .expect("should detect the host platform successfully"),
+                host: host_current_with_libdir(host_libdir.as_ref()),
                 target: Some(TargetPlatform::new(
                     TargetTriple::x86_64_unknown_linux_gnu(),
                     PlatformLibdir::Available(target_libdir.clone()),
diff --git a/nextest-runner/src/platform.rs b/nextest-runner/src/platform.rs
index a842a082c86..c32d556a316 100644
--- a/nextest-runner/src/platform.rs
+++ b/nextest-runner/src/platform.rs
@@ -5,17 +5,23 @@
 
 use crate::{
     cargo_config::{CargoTargetArg, TargetTriple},
-    errors::{RustBuildMetaParseError, TargetTripleError, UnknownHostPlatform},
+    errors::{
+        DisplayErrorChain, HostPlatformDetectError, RustBuildMetaParseError, TargetTripleError,
+    },
     reuse_build::{LibdirMapper, PlatformLibdirMapper},
+    RustcCli,
 };
 use camino::{Utf8Path, Utf8PathBuf};
+use indent_write::indentable::Indented;
 use nextest_metadata::{
     BuildPlatformsSummary, HostPlatformSummary, PlatformLibdirSummary, PlatformLibdirUnavailable,
     TargetPlatformSummary,
 };
-use target_spec::summaries::PlatformSummary;
 pub use target_spec::Platform;
-use tracing::debug;
+use target_spec::{
+    errors::RustcVersionVerboseParseError, summaries::PlatformSummary, TargetFeatures,
+};
+use tracing::{debug, warn};
 
 /// A representation of host and target platform.
 #[derive(Clone, Debug, Eq, PartialEq)]
@@ -33,11 +39,18 @@ impl BuildPlatforms {
     /// Creates a new `BuildPlatforms` with no libdirs or targets.
     ///
     /// Used for testing.
-    pub fn new_with_no_target() -> Result<Self, UnknownHostPlatform> {
+    pub fn new_with_no_target() -> Result<Self, HostPlatformDetectError> {
         Ok(Self {
-            host: HostPlatform::current(PlatformLibdir::Unavailable(
-                PlatformLibdirUnavailable::new_const("test"),
-            ))?,
+            host: HostPlatform {
+                // Because this is for testing, we just use the build target
+                // rather than `rustc -vV` output.
+                platform: Platform::build_target().map_err(|build_target_error| {
+                    HostPlatformDetectError::BuildTargetError {
+                        build_target_error: Box::new(build_target_error),
+                    }
+                })?,
+                libdir: PlatformLibdir::Unavailable(PlatformLibdirUnavailable::new_const("test")),
+            },
             target: None,
         })
     }
@@ -127,10 +140,13 @@ impl BuildPlatforms {
         // * the host might be serialized as the target platform as well (we can't detect this case
         //   reliably, so we treat it as the target platform as well, which isn't a problem in
         //   practice).
-        let host = HostPlatform::current(PlatformLibdir::Unavailable(
-            PlatformLibdirUnavailable::OLD_SUMMARY,
-        ))
-        .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?;
+        let host = HostPlatform {
+            // We don't necessarily have `rustc` available, so we use the build
+            // target instead.
+            platform: Platform::build_target()
+                .map_err(RustBuildMetaParseError::DetectBuildTargetError)?,
+            libdir: PlatformLibdir::Unavailable(PlatformLibdirUnavailable::OLD_SUMMARY),
+        };
 
         let target = TargetTriple::deserialize(Some(summary))?.map(|triple| {
             TargetPlatform::new(
@@ -153,10 +169,13 @@ impl BuildPlatforms {
         // * the host might be serialized as the target platform as well (we can't detect this case
         //   reliably, so we treat it as the target platform as well, which isn't a problem in
         //   practice).
-        let host = HostPlatform::current(PlatformLibdir::Unavailable(
-            PlatformLibdirUnavailable::OLD_SUMMARY,
-        ))
-        .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?;
+        let host = HostPlatform {
+            // We don't necessarily have `rustc` available, so we use the build
+            // target instead.
+            platform: Platform::build_target()
+                .map_err(RustBuildMetaParseError::DetectBuildTargetError)?,
+            libdir: PlatformLibdir::Unavailable(PlatformLibdirUnavailable::OLD_SUMMARY),
+        };
 
         let target = TargetTriple::deserialize_str(summary)?.map(|triple| {
             TargetPlatform::new(
@@ -180,9 +199,12 @@ pub struct HostPlatform {
 }
 
 impl HostPlatform {
-    /// Creates a new `HostPlatform` representing the current platform.
-    pub fn current(libdir: PlatformLibdir) -> Result<Self, UnknownHostPlatform> {
-        let platform = Platform::current().map_err(|error| UnknownHostPlatform { error })?;
+    /// Creates a new `HostPlatform` representing the current platform by
+    /// querying rustc.
+    ///
+    /// This may fall back to the build target if `rustc -vV` fails.
+    pub fn detect(libdir: PlatformLibdir) -> Result<Self, HostPlatformDetectError> {
+        let platform = detect_host_platform()?;
         Ok(Self { platform, libdir })
     }
 
@@ -214,6 +236,137 @@ impl HostPlatform {
     }
 }
 
+/// Detect the host platform by using `rustc -vV`, and falling back to the build
+/// target.
+///
+/// Returns an error if both of those methods fail, and produces a warning if
+/// `rustc -vV` fails.
+fn detect_host_platform() -> Result<Platform, HostPlatformDetectError> {
+    // A test-only environment variable to always make the build target a fixed
+    // value, or to error out.
+    const FORCE_BUILD_TARGET_VAR: &str = "__NEXTEST_FORCE_BUILD_TARGET";
+
+    enum ForceBuildTarget {
+        Triple(String),
+        Error,
+    }
+
+    let force_build_target = match std::env::var(FORCE_BUILD_TARGET_VAR).as_deref() {
+        Ok("error") => Some(ForceBuildTarget::Error),
+        Ok(triple) => Some(ForceBuildTarget::Triple(triple.to_owned())),
+        Err(_) => None,
+    };
+
+    let build_target = match force_build_target {
+        Some(ForceBuildTarget::Triple(triple)) => Platform::new(triple, TargetFeatures::Unknown),
+        Some(ForceBuildTarget::Error) => Err(target_spec::Error::RustcVersionVerboseParse(
+            RustcVersionVerboseParseError::MissingHostLine {
+                output: format!(
+                    "({} set to \"error\", forcibly failing build target detection)\n",
+                    FORCE_BUILD_TARGET_VAR
+                ),
+            },
+        )),
+        None => Platform::build_target(),
+    };
+
+    let rustc_vv = RustcCli::version_verbose()
+        .to_expression()
+        .stdout_capture()
+        .stderr_capture()
+        .unchecked();
+    match rustc_vv.run() {
+        Ok(output) => {
+            if output.status.success() {
+                // Neither `rustc` nor `cargo` tell us what target features are
+                // enabled for the host, so we must use
+                // `TargetFeatures::Unknown`.
+                match Platform::from_rustc_version_verbose(output.stdout, TargetFeatures::Unknown) {
+                    Ok(platform) => Ok(platform),
+                    Err(host_platform_error) => {
+                        match build_target {
+                            Ok(build_target) => {
+                                warn!(
+                                    "for host platform, parsing `rustc -vV` failed; \
+                                     falling back to build target `{}`\n\
+                                     - host platform error:\n{}",
+                                    build_target.triple().as_str(),
+                                    DisplayErrorChain::new_with_initial_indent(
+                                        "  ",
+                                        host_platform_error
+                                    ),
+                                );
+                                Ok(build_target)
+                            }
+                            Err(build_target_error) => {
+                                // In this case, we can't do anything.
+                                Err(HostPlatformDetectError::HostPlatformParseError {
+                                    host_platform_error: Box::new(host_platform_error),
+                                    build_target_error: Box::new(build_target_error),
+                                })
+                            }
+                        }
+                    }
+                }
+            } else {
+                match build_target {
+                    Ok(build_target) => {
+                        warn!(
+                            "for host platform, `rustc -vV` failed with {}; \
+                             falling back to build target `{}`\n\
+                             - `rustc -vV` stdout:\n{}\n\
+                             - `rustc -vV` stderr:\n{}",
+                            output.status,
+                            build_target.triple().as_str(),
+                            Indented {
+                                item: String::from_utf8_lossy(&output.stdout),
+                                indent: "  "
+                            },
+                            Indented {
+                                item: String::from_utf8_lossy(&output.stderr),
+                                indent: "  "
+                            },
+                        );
+                        Ok(build_target)
+                    }
+                    Err(build_target_error) => {
+                        // If the build target isn't available either, we
+                        // can't do anything.
+                        Err(HostPlatformDetectError::RustcVvFailed {
+                            status: output.status,
+                            stdout: output.stdout,
+                            stderr: output.stderr,
+                            build_target_error: Box::new(build_target_error),
+                        })
+                    }
+                }
+            }
+        }
+        Err(error) => {
+            match build_target {
+                Ok(build_target) => {
+                    warn!(
+                        "for host platform, failed to spawn `rustc -vV`; \
+                         falling back to build target `{}`\n\
+                         - host platform error:\n{}",
+                        build_target.triple().as_str(),
+                        DisplayErrorChain::new_with_initial_indent("  ", error),
+                    );
+                    Ok(build_target)
+                }
+                Err(build_target_error) => {
+                    // If the build target isn't available either, we
+                    // can't do anything.
+                    Err(HostPlatformDetectError::RustcVvSpawnError {
+                        error,
+                        build_target_error: Box::new(build_target_error),
+                    })
+                }
+            }
+        }
+    }
+}
+
 /// The target platform.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct TargetPlatform {
diff --git a/nextest-runner/src/rustc_cli.rs b/nextest-runner/src/rustc_cli.rs
index bc0241367d3..4b50c095b4c 100644
--- a/nextest-runner/src/rustc_cli.rs
+++ b/nextest-runner/src/rustc_cli.rs
@@ -14,6 +14,13 @@ pub struct RustcCli<'a> {
 }
 
 impl<'a> RustcCli<'a> {
+    /// Create a rustc CLI call: `rustc --version --verbose`.
+    pub fn version_verbose() -> Self {
+        let mut cli = Self::default();
+        cli.add_arg("--version").add_arg("--verbose");
+        cli
+    }
+
     /// Create a rustc CLI call: `rustc --print target-libdir`.
     pub fn print_host_libdir() -> Self {
         let mut cli = Self::default();
@@ -36,7 +43,8 @@ impl<'a> RustcCli<'a> {
         self
     }
 
-    fn to_expression(&self) -> duct::Expression {
+    /// Convert the command to a [`duct::Expression`].
+    pub fn to_expression(&self) -> duct::Expression {
         duct::cmd(
             self.rustc_path.as_str(),
             self.args.iter().map(|arg| arg.as_ref()),
diff --git a/nextest-runner/src/update.rs b/nextest-runner/src/update.rs
index ddfc08fe424..9cb61bab551 100644
--- a/nextest-runner/src/update.rs
+++ b/nextest-runner/src/update.rs
@@ -222,7 +222,10 @@ impl NextestReleases {
     }
 
     fn target_triple(&self) -> String {
-        let current = Platform::current().expect("current platform could not be detected");
+        // In this case, use the build target, *not* `rustc -vV` output. This
+        // ensures that e.g. musl binary updates continue to use the musl
+        // target.
+        let current = Platform::build_target().expect("build target could not be detected");
         let triple_str = current.triple_str();
         if triple_str.ends_with("-apple-darwin") {
             // Nextest builds a universal binary for Mac.
diff --git a/nextest-runner/tests/integration/target_runner.rs b/nextest-runner/tests/integration/target_runner.rs
index 746d295cdab..f7a79530b89 100644
--- a/nextest-runner/tests/integration/target_runner.rs
+++ b/nextest-runner/tests/integration/target_runner.rs
@@ -31,7 +31,7 @@ fn runner_for_target(triple: Option<&str>) -> Result<(BuildPlatforms, TargetRunn
     .unwrap();
 
     let build_platforms = {
-        let host = HostPlatform::current(PlatformLibdir::from_rustc_stdout(
+        let host = HostPlatform::detect(PlatformLibdir::from_rustc_stdout(
             RustcCli::print_host_libdir().read(),
         ))?;
         let target = if let Some(triple) = TargetTriple::find(&configs, triple)? {
@@ -170,7 +170,7 @@ fn passthrough_path() -> &'static Utf8Path {
 
 fn current_runner_env_var() -> String {
     PlatformRunner::runner_env_var(
-        &Platform::current().expect("current platform is known to target-spec"),
+        &Platform::build_target().expect("current platform is known to target-spec"),
     )
 }