From c7536ff6b5a06606311327b5eedb4cfdb62d3723 Mon Sep 17 00:00:00 2001 From: Kaiyi Li Date: Fri, 17 May 2024 20:45:31 -0700 Subject: [PATCH 1/5] build: add RUSTUP_WINDOWS_PATH_ADD_BIN: 1 ... to WA https://github.com/nextest-rs/nextest/issues/1493 for the "Test with latest nextest release" step on Windows temporarily. --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0804e91fd9b..24c9d87f41a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,10 @@ jobs: run: cargo local-nt run --profile ci - name: Test with latest nextest release run: cargo nextest run --profile ci + env: + # TODO: should remove once https://github.com/nextest-rs/nextest/pull/1499 is in the + # latest release + RUSTUP_WINDOWS_PATH_ADD_BIN: 1 - name: Test without double-spawning if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-14' }} env: From a6d8c6c124e4e4dab2180f3d105cad93e99be848 Mon Sep 17 00:00:00 2001 From: Kaiyi Li Date: Sun, 12 May 2024 12:54:17 -0700 Subject: [PATCH 2/5] Refactor that prepares to add rustc libdir to `BuildPlatforms` * Add a `BuildPlatformsTarget` to store the target triple. We will store target libdir in this struct in a following commit. * Change `BuildPlatforms::new` to be called without any arguments, which make it easer to create `BuildPlatforms` when there is no target, especially for testing. * Create a serialized form of `BuildPlatforms`: `BuildPlatformsSummary` * Change `discover_build_platforms` to `discover_target_triple` which only discovers the `TargetTriple` --- cargo-nextest/src/dispatch.rs | 25 +- nextest-metadata/src/test_list.rs | 37 ++- .../src/cargo_config/target_triple.rs | 12 + nextest-runner/src/config/overrides.rs | 4 +- nextest-runner/src/config/scripts.rs | 4 +- nextest-runner/src/config/test_helpers.rs | 18 +- nextest-runner/src/errors.rs | 7 + nextest-runner/src/list/binary_list.rs | 30 ++- nextest-runner/src/list/rust_build_meta.rs | 249 ++++++++++++++++-- nextest-runner/src/list/test_list.rs | 26 +- nextest-runner/src/platform.rs | 154 +++++++++-- nextest-runner/src/reporter.rs | 2 +- nextest-runner/src/runner.rs | 2 +- nextest-runner/src/target_runner.rs | 2 +- nextest-runner/tests/integration/basic.rs | 10 +- nextest-runner/tests/integration/fixtures.rs | 2 +- .../tests/integration/target_runner.rs | 8 +- 17 files changed, 512 insertions(+), 80 deletions(-) diff --git a/cargo-nextest/src/dispatch.rs b/cargo-nextest/src/dispatch.rs index e47207b66d1..1570c8a33a8 100644 --- a/cargo-nextest/src/dispatch.rs +++ b/cargo-nextest/src/dispatch.rs @@ -21,13 +21,13 @@ use nextest_runner::{ VersionOnlyConfig, }, double_spawn::DoubleSpawnInfo, - errors::{UnknownHostPlatform, WriteTestListError}, + errors::WriteTestListError, list::{ BinaryList, OutputFormat, RustTestArtifact, SerializableFormat, TestExecuteContext, TestList, }, partition::PartitionerBuilder, - platform::BuildPlatforms, + platform::{BuildPlatforms, BuildPlatformsTarget}, redact::Redactor, reporter::{structured, FinalStatusLevel, StatusLevel, TestOutputDisplay, TestReporterBuilder}, reuse_build::{archive_to_file, ArchiveReporter, PathMapper, ReuseBuildInfo}, @@ -971,6 +971,7 @@ impl From for FinalStatusLevel { #[derive(Debug)] struct BaseApp { output: OutputContext, + // TODO: support multiple --target options build_platforms: BuildPlatforms, cargo_metadata_json: Arc, package_graph: Arc, @@ -1006,7 +1007,15 @@ 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 => discover_build_platforms(&cargo_configs, cargo_opts.target.as_deref())?, + None => { + let mut build_platforms = BuildPlatforms::new()?; + if let Some(triple) = + discover_target_triple(&cargo_configs, cargo_opts.target.as_deref()) + { + build_platforms.target = Some(BuildPlatformsTarget { triple }); + } + build_platforms + } }; // Read the Cargo metadata. @@ -1927,11 +1936,11 @@ fn acquire_graph_data( Ok(json) } -fn discover_build_platforms( +fn discover_target_triple( cargo_configs: &CargoConfigs, target_cli_option: Option<&str>, -) -> Result { - let target_triple = match TargetTriple::find(cargo_configs, target_cli_option) { +) -> Option { + match TargetTriple::find(cargo_configs, target_cli_option) { Ok(Some(triple)) => { log::debug!( "using target triple `{}` defined by `{}`; {}", @@ -1950,9 +1959,7 @@ fn discover_build_platforms( warn_on_err("target triple", &err); None } - }; - - BuildPlatforms::new(target_triple) + } } fn runner_for_target( diff --git a/nextest-metadata/src/test_list.rs b/nextest-metadata/src/test_list.rs index 909a9fd3696..844bbd84c88 100644 --- a/nextest-metadata/src/test_list.rs +++ b/nextest-metadata/src/test_list.rs @@ -461,7 +461,7 @@ pub enum RustBinaryIdNameAndKind<'a> { } /// Rust metadata used for builds and test runs. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)] #[serde(rename_all = "kebab-case")] pub struct RustBuildMetaSummary { /// The target directory for Rust artifacts. @@ -483,7 +483,13 @@ pub struct RustBuildMetaSummary { /// Linked paths, relative to the target directory. pub linked_paths: BTreeSet, + /// The build platforms used while compiling the Rust artifacts. + #[serde(default)] + pub platforms: Option, + /// The target platforms used while compiling the Rust artifacts. + /// + /// Deprecated in favor of [`Self::platforms`]; use that if non-empty. #[serde(default)] pub target_platforms: Vec, @@ -510,6 +516,33 @@ pub struct RustNonTestBinarySummary { pub path: Utf8PathBuf, } +/// Serialized representation of the host platform. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct HostPlatformSummary { + /// The host platform, if specified. + pub platform: PlatformSummary, +} + +/// Serialized representation of the target platform. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct TargetPlatformSummary { + /// The target platform, if specified. + pub platform: PlatformSummary, +} + +/// Serialized representation of the host and the target platform. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct BuildPlatformsSummary { + /// The target platform used while compiling the Rust artifacts. + pub host: HostPlatformSummary, + + /// The host platform used while compiling the Rust artifacts. + pub targets: Vec, +} + /// Information about the kind of a Rust non-test binary. /// /// This is part of [`RustNonTestBinarySummary`], and is used to determine runtime environment @@ -705,6 +738,7 @@ mod tests { linked_paths: BTreeSet::new(), target_platform: None, target_platforms: vec![], + platforms: None, }; "no target platform")] #[test_case(r#"{ "target-directory": "/foo", @@ -720,6 +754,7 @@ mod tests { linked_paths: BTreeSet::new(), target_platform: Some("x86_64-unknown-linux-gnu".to_owned()), target_platforms: vec![], + platforms: None, }; "single target platform specified")] fn test_deserialize_old_rust_build_meta(input: &str, expected: RustBuildMetaSummary) { let build_meta: RustBuildMetaSummary = diff --git a/nextest-runner/src/cargo_config/target_triple.rs b/nextest-runner/src/cargo_config/target_triple.rs index 06bb33ebfce..ec2e7daf043 100644 --- a/nextest-runner/src/cargo_config/target_triple.rs +++ b/nextest-runner/src/cargo_config/target_triple.rs @@ -24,6 +24,18 @@ pub struct TargetTriple { } impl TargetTriple { + /// Create an x86_64-unknown-linux-gnu [`TargetTriple`]. Useful for testing. + /// + /// # Panics + /// + /// Panics if the underlying implementation fail to parse the `"x86_64-unknown-linux-gnu"` + /// triple string. + pub fn x86_64_unknown_linux_gnu() -> Self { + TargetTriple::deserialize_str(Some("x86_64-unknown-linux-gnu".to_owned())) + .expect("creating TargetTriple from linux gnu triple string should succeed") + .expect("the output of deserialize_str shouldn't be None") + } + /// Converts a `PlatformSummary` that was output by `TargetTriple::serialize` back to a target triple. /// This target triple is assumed to originate from a build-metadata config. pub fn deserialize( diff --git a/nextest-runner/src/config/overrides.rs b/nextest-runner/src/config/overrides.rs index bb24a6680c9..1c94501b354 100644 --- a/nextest-runner/src/config/overrides.rs +++ b/nextest-runner/src/config/overrides.rs @@ -482,8 +482,8 @@ impl CompiledOverride { let target_eval = build_platforms .target .as_ref() - .map_or(host_test_eval, |triple| { - self.data.target_spec.eval(&triple.platform) + .map_or(host_test_eval, |target| { + self.data.target_spec.eval(&target.triple.platform) }); CompiledOverride { diff --git a/nextest-runner/src/config/scripts.rs b/nextest-runner/src/config/scripts.rs index 6869b981a28..db709c45596 100644 --- a/nextest-runner/src/config/scripts.rs +++ b/nextest-runner/src/config/scripts.rs @@ -388,8 +388,8 @@ impl CompiledProfileScripts { let target_eval = build_platforms .target .as_ref() - .map_or(host_test_eval, |triple| { - self.data.target_spec.eval(&triple.platform) + .map_or(host_test_eval, |target| { + self.data.target_spec.eval(&target.triple.platform) }); CompiledProfileScripts { diff --git a/nextest-runner/src/config/test_helpers.rs b/nextest-runner/src/config/test_helpers.rs index 6c86ccd6e6f..8bd100c3f85 100644 --- a/nextest-runner/src/config/test_helpers.rs +++ b/nextest-runner/src/config/test_helpers.rs @@ -4,7 +4,7 @@ use crate::{ cargo_config::{TargetDefinitionLocation, TargetTriple, TargetTripleSource}, config::{CustomTestGroup, TestGroup}, - platform::BuildPlatforms, + platform::{BuildPlatforms, BuildPlatformsTarget}, }; use camino::{Utf8Path, Utf8PathBuf}; use guppy::{ @@ -84,14 +84,16 @@ pub(super) fn binary_query<'a>( } pub(super) fn build_platforms() -> BuildPlatforms { - BuildPlatforms::new_with_host( - Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(), - Some(TargetTriple { - platform: Platform::new("aarch64-apple-darwin", TargetFeatures::Unknown).unwrap(), - source: TargetTripleSource::Env, - location: TargetDefinitionLocation::Builtin, + BuildPlatforms { + host: Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(), + target: Some(BuildPlatformsTarget { + triple: TargetTriple { + platform: Platform::new("aarch64-apple-darwin", TargetFeatures::Unknown).unwrap(), + source: TargetTripleSource::Env, + location: TargetDefinitionLocation::Builtin, + }, }), - ) + } } pub(super) fn test_group(name: &str) -> TestGroup { diff --git a/nextest-runner/src/errors.rs b/nextest-runner/src/errors.rs index 57ccf3eae1d..6a5c245eb31 100644 --- a/nextest-runner/src/errors.rs +++ b/nextest-runner/src/errors.rs @@ -563,6 +563,13 @@ pub enum RustBuildMetaParseError { /// The host platform could not be determined. #[error("the host platform could not be determined")] UnknownHostPlatform(#[source] target_spec::Error), + + /// The build metadata includes features unsupported. + #[error("unsupported features in the build metadata: {message}")] + Unsupported { + /// The detailed error message. + message: String, + }, } /// An error that occurs in [`BinaryList::from_messages`](crate::list::BinaryList::from_messages) or diff --git a/nextest-runner/src/list/binary_list.rs b/nextest-runner/src/list/binary_list.rs index 4190f89d11f..5c4924bb8a5 100644 --- a/nextest-runner/src/list/binary_list.rs +++ b/nextest-runner/src/list/binary_list.rs @@ -394,6 +394,7 @@ mod tests { use crate::{ cargo_config::{TargetDefinitionLocation, TargetTriple, TargetTripleSource}, list::SerializableFormat, + platform::BuildPlatformsTarget, }; use indoc::indoc; use maplit::btreeset; @@ -422,11 +423,16 @@ mod tests { }; let fake_triple = TargetTriple { - platform: Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(), + platform: Platform::new("aarch64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(), source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; - let build_platforms = BuildPlatforms::new(Some(fake_triple)).unwrap(); + let build_platforms = BuildPlatforms { + host: TargetTriple::x86_64_unknown_linux_gnu().platform, + target: Some(BuildPlatformsTarget { + triple: fake_triple, + }), + }; let mut rust_build_meta = RustBuildMeta::new("/fake/target", build_platforms); rust_build_meta @@ -499,13 +505,29 @@ mod tests { }, "build-script-out-dirs": {}, "linked-paths": [], + "platforms": { + "host": { + "platform": { + "triple": "x86_64-unknown-linux-gnu", + "target-features": "unknown" + } + }, + "targets": [ + { + "platform": { + "triple": "aarch64-unknown-linux-gnu", + "target-features": "unknown" + } + } + ] + }, "target-platforms": [ { - "triple": "x86_64-unknown-linux-gnu", + "triple": "aarch64-unknown-linux-gnu", "target-features": "unknown" } ], - "target-platform": "x86_64-unknown-linux-gnu" + "target-platform": "aarch64-unknown-linux-gnu" }, "rust-binaries": { "fake-macro::proc-macro/fake-macro": { diff --git a/nextest-runner/src/list/rust_build_meta.rs b/nextest-runner/src/list/rust_build_meta.rs index ca5bf75e543..e2b4df8a153 100644 --- a/nextest-runner/src/list/rust_build_meta.rs +++ b/nextest-runner/src/list/rust_build_meta.rs @@ -2,15 +2,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - cargo_config::TargetTriple, errors::RustBuildMetaParseError, helpers::convert_rel_path_to_main_sep, list::{BinaryListState, TestListState}, - platform::BuildPlatforms, + platform::{BuildPlatforms, FromSummary, ToSummary}, reuse_build::PathMapper, }; use camino::Utf8PathBuf; -use nextest_metadata::{RustBuildMetaSummary, RustNonTestBinarySummary}; +use nextest_metadata::{BuildPlatformsSummary, RustBuildMetaSummary, RustNonTestBinarySummary}; use std::{ collections::{BTreeMap, BTreeSet}, marker::PhantomData, @@ -135,15 +134,15 @@ impl RustBuildMeta { impl RustBuildMeta { /// Creates a `RustBuildMeta` from a serializable summary. pub fn from_summary(summary: RustBuildMetaSummary) -> Result { - let target_triple = if !summary.target_platforms.is_empty() { - // TODO: support multiple --target options - TargetTriple::deserialize(summary.target_platforms.first().cloned())? + let build_platforms = if let Some(summary) = summary.platforms { + BuildPlatforms::from_summary(summary.clone())? + } else if let Some(summary) = summary.target_platforms.first() { + // Compatibility with metadata generated by older versions of nextest. + BuildPlatforms::from_summary(summary.clone())? } else { // Compatibility with metadata generated by older versions of nextest. - TargetTriple::deserialize_str(summary.target_platform)? + BuildPlatforms::from_summary_str(summary.target_platform.clone())? }; - let build_platforms = BuildPlatforms::new(target_triple) - .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?; Ok(Self { target_directory: summary.target_directory, @@ -168,13 +167,233 @@ impl RustBuildMeta { non_test_binaries: self.non_test_binaries.clone(), build_script_out_dirs: self.build_script_out_dirs.clone(), linked_paths: self.linked_paths.keys().cloned().collect(), - target_platform: self - .build_platforms - .target - .as_ref() - .map(|triple| triple.platform.triple_str().to_owned()), - // TODO: support multiple --target options + target_platform: self.build_platforms.to_summary_str(), target_platforms: vec![self.build_platforms.to_summary()], + // TODO: support multiple --target options + platforms: Some(BuildPlatformsSummary { + host: self.build_platforms.to_summary(), + targets: self + .build_platforms + .target + .as_ref() + .into_iter() + .map(ToSummary::to_summary) + .collect(), + }), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cargo_config::TargetTriple, + platform::{BuildPlatforms, BuildPlatformsTarget}, + }; + use nextest_metadata::{BuildPlatformsSummary, HostPlatformSummary, TargetPlatformSummary}; + use target_spec::{summaries::PlatformSummary, Platform}; + use test_case::test_case; + + impl Default for RustBuildMeta { + fn default() -> Self { + RustBuildMeta::::new( + Utf8PathBuf::default(), + BuildPlatforms::new() + .expect("creating BuildPlatforms without target triple should succeed"), + ) + } + } + + fn x86_64_pc_windows_msvc_triple() -> TargetTriple { + TargetTriple::deserialize_str(Some("x86_64-pc-windows-msvc".to_owned())) + .expect("creating TargetTriple from windows msvc triple string should succeed") + .expect("the output of deserialize_str shouldn't be None") + } + + fn x86_64_apple_darwin_triple() -> TargetTriple { + TargetTriple::deserialize_str(Some("x86_64-apple-darwin".to_owned())) + .expect("creating TargetTriple from apple triple string should succeed") + .expect("the output of deserialize_str shouldn't be None") + } + + fn aarch64_unknown_linux_gnu_triple() -> TargetTriple { + TargetTriple::deserialize_str(Some("aarch64-unknown-linux-gnu".to_owned())) + .expect("creating TargetTriple from ARM Linux triple string should succeed") + .expect("the output of deserialize_str shouldn't be None") + } + + fn host_platform() -> Platform { + Platform::current().expect("should detect the host platform successfully") + } + + #[test_case(RustBuildMetaSummary { + ..Default::default() + }, RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: host_platform(), + target: None, + }, + ..Default::default() + }; "no target platforms")] + #[test_case(RustBuildMetaSummary { + target_platform: Some("x86_64-unknown-linux-gnu".to_owned()), + ..Default::default() + }, RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: host_platform(), + target: Some(BuildPlatformsTarget{ + triple: TargetTriple::x86_64_unknown_linux_gnu(), + }), + }, + ..Default::default() + }; "only target platform field")] + #[test_case(RustBuildMetaSummary { + target_platform: Some("x86_64-unknown-linux-gnu".to_owned()), + target_platforms: vec![PlatformSummary::new("x86_64-pc-windows-msvc")], + ..Default::default() + }, RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: host_platform(), + target: Some(BuildPlatformsTarget{ + triple: x86_64_pc_windows_msvc_triple() + }), + }, + ..Default::default() + }; "target platform and target platforms field")] + #[test_case(RustBuildMetaSummary { + target_platform: Some("x86_64-unknown-linux-gnu".to_owned()), + target_platforms: vec![PlatformSummary::new("x86_64-pc-windows-msvc")], + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: not_host_platform_triple().platform.to_summary(), + }, + targets: vec![TargetPlatformSummary { + platform: PlatformSummary::new("aarch64-unknown-linux-gnu"), + }], + }), + ..Default::default() + }, RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: not_host_platform_triple().platform, + target: Some(BuildPlatformsTarget{ + triple: aarch64_unknown_linux_gnu_triple(), + }), + }, + ..Default::default() + }; "target platform and target platforms and platforms field")] + #[test_case(RustBuildMetaSummary { + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: PlatformSummary::new("x86_64-apple-darwin"), + }, + targets: vec![], + }), + ..Default::default() + }, RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: x86_64_apple_darwin_triple().platform, + target: None, + }, + ..Default::default() + }; "platforms with zero targets")] + fn test_from_summary(summary: RustBuildMetaSummary, expected: RustBuildMeta) { + let actual = RustBuildMeta::::from_summary(summary) + .expect("RustBuildMeta should deserialize from summary with success."); + assert_eq!(actual, expected); + } + + #[test] + fn test_from_summary_error_multiple_targets() { + let summary = RustBuildMetaSummary { + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: PlatformSummary::new("x86_64-apple-darwin"), + }, + targets: vec![ + TargetPlatformSummary { + platform: PlatformSummary::new("aarch64-unknown-linux-gnu"), + }, + TargetPlatformSummary { + platform: PlatformSummary::new("x86_64-pc-windows-msvc"), + }, + ], + }), + ..Default::default() + }; + let actual = RustBuildMeta::::from_summary(summary); + assert!(matches!(actual, Err(RustBuildMetaParseError::Unsupported { .. })), "Expect the parse result to be an error of RustBuildMetaParseError::Unsupported, actual {:?}", actual); + } + + #[test] + fn test_from_summary_error_invalid_host_platform_summary() { + let summary = RustBuildMetaSummary { + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: PlatformSummary::new("invalid-platform-triple"), + }, + targets: vec![], + }), + ..Default::default() + }; + let actual = RustBuildMeta::::from_summary(summary); + assert!( + actual.is_err(), + "Expect the parse result to be an error, actual {:?}", + actual + ); + } + + fn not_host_platform_triple() -> TargetTriple { + cfg_if::cfg_if! { + if #[cfg(windows)] { + TargetTriple::x86_64_unknown_linux_gnu() + } else { + x86_64_pc_windows_msvc_triple() + } } } + + #[test_case(RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: host_platform(), + target: None, + }, + ..Default::default() + }, RustBuildMetaSummary { + target_platform: None, + target_platforms: vec![host_platform().to_summary()], + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: host_platform().to_summary() + }, + targets: vec![], + }), + ..Default::default() + }; "build platforms without target")] + #[test_case(RustBuildMeta:: { + build_platforms: BuildPlatforms { + host: host_platform(), + target: Some(BuildPlatformsTarget { + triple: not_host_platform_triple() + }), + }, + ..Default::default() + }, RustBuildMetaSummary { + target_platform: Some(not_host_platform_triple().platform.triple_str().to_owned()), + target_platforms: vec![not_host_platform_triple().platform.to_summary()], + platforms: Some(BuildPlatformsSummary { + host: HostPlatformSummary { + platform: host_platform().to_summary(), + }, + targets: vec![TargetPlatformSummary { + platform: not_host_platform_triple().platform.to_summary(), + }], + }), + ..Default::default() + }; "build platforms with target")] + fn test_to_summary(meta: RustBuildMeta, expected: RustBuildMetaSummary) { + let actual = meta.to_summary(); + assert_eq!(actual, expected); + } } diff --git a/nextest-runner/src/list/test_list.rs b/nextest-runner/src/list/test_list.rs index 8ae64c76d6d..183ee1f86b2 100644 --- a/nextest-runner/src/list/test_list.rs +++ b/nextest-runner/src/list/test_list.rs @@ -959,7 +959,7 @@ mod tests { use crate::{ cargo_config::{TargetDefinitionLocation, TargetTriple, TargetTripleSource}, list::SerializableFormat, - platform::BuildPlatforms, + platform::{BuildPlatforms, BuildPlatformsTarget}, test_filter::RunIgnored, }; use guppy::CargoMetadata; @@ -1033,7 +1033,13 @@ mod tests { source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; - let build_platforms = BuildPlatforms::new(Some(fake_triple)).unwrap(); + let build_platforms = BuildPlatforms { + host: TargetTriple::x86_64_unknown_linux_gnu().platform, + target: Some(BuildPlatformsTarget { + triple: fake_triple, + }), + }; + let fake_env = EnvironmentMap::empty(); let rust_build_meta = RustBuildMeta::new("/fake", build_platforms).map_paths(&PathMapper::noop()); @@ -1139,6 +1145,22 @@ mod tests { "non-test-binaries": {}, "build-script-out-dirs": {}, "linked-paths": [], + "platforms": { + "host": { + "platform": { + "triple": "x86_64-unknown-linux-gnu", + "target-features": "unknown" + } + }, + "targets": [ + { + "platform": { + "triple": "aarch64-unknown-linux-gnu", + "target-features": "unknown" + } + } + ] + }, "target-platforms": [ { "triple": "aarch64-unknown-linux-gnu", diff --git a/nextest-runner/src/platform.rs b/nextest-runner/src/platform.rs index 5ecb187131a..7ea0635ffe0 100644 --- a/nextest-runner/src/platform.rs +++ b/nextest-runner/src/platform.rs @@ -5,44 +5,42 @@ use crate::{ cargo_config::{CargoTargetArg, TargetTriple}, - errors::{TargetTripleError, UnknownHostPlatform}, + errors::{RustBuildMetaParseError, TargetTripleError, UnknownHostPlatform}, }; +use nextest_metadata::{BuildPlatformsSummary, HostPlatformSummary, TargetPlatformSummary}; use target_spec::summaries::PlatformSummary; pub use target_spec::Platform; -/// A representation of host and target platforms. +/// The target platform. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BuildPlatformsTarget { + /// The target triplet, which consists of machine, vendor and OS. + pub triple: TargetTriple, +} + +/// A representation of host and target platform. #[derive(Clone, Debug, Eq, PartialEq)] pub struct BuildPlatforms { /// The host platform. pub host: Platform, /// The target platform, if specified. - /// - /// In the future, this will become a list of target triples once multiple `--target` arguments - /// are supported. - pub target: Option, + pub target: Option, } impl BuildPlatforms { - /// Creates a new `BuildPlatform`. + /// Creates a new [`BuildPlatforms`]. /// /// Returns an error if the host platform could not be determined. - pub fn new(target: Option) -> Result { + pub fn new() -> Result { let host = Platform::current().map_err(|error| UnknownHostPlatform { error })?; - Ok(Self { host, target }) - } - - /// Creates a new `BuildPlatform` where the host is specified. - /// - /// This is intended for testing situations. Most users should call [`Self::new`] instead. - pub fn new_with_host(host: Platform, target: Option) -> Self { - Self { host, target } + Ok(Self { host, target: None }) } /// Returns the argument to pass into `cargo metadata --filter-platform `. pub fn to_cargo_target_arg(&self) -> Result { match &self.target { - Some(target) => target.to_cargo_target_arg(), + Some(target) => target.triple.to_cargo_target_arg(), None => { // If there's no target, use the host platform. Ok(CargoTargetArg::Builtin(self.host.triple_str().to_owned())) @@ -50,19 +48,125 @@ impl BuildPlatforms { } } - /// Converts a target triple to a `String` that can be stored in the build-metadata. + /// Converts a target triple to a [`String`] that can be stored in the build-metadata. /// - /// cargo-nextest represents the host triple with `None` during runtime. However the - /// build-metadata might be used on a system with a different host triple. Therefore, the host - /// triple is detected if `target_triple` is `None`. + /// Only for backward compatibility. Deprecated in favor of [`BuildPlatformsSummary`]. + pub fn to_summary_str(&self) -> Option { + self.target + .as_ref() + .map(|triple| triple.triple.platform.triple_str().to_owned()) + } + + /// Creates a [`BuildPlatforms`] from a serializable summary. /// - /// XXX: This isn't quite correct -- instead, we should serialize this into our own - /// `BuildPlatformsSummary`. - pub fn to_summary(&self) -> PlatformSummary { + /// Only for backward compatibility. Deprecated in favor of [`BuildPlatformsSummary`]. + pub fn from_summary_str(summary: Option) -> Result { + let mut build_platforms = BuildPlatforms::new() + .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?; + if let Some(triple) = TargetTriple::deserialize_str(summary)? { + build_platforms.target = Some(BuildPlatformsTarget { triple }); + } + Ok(build_platforms) + } +} + +/// A value-to-value conversion that consumes the input value and generate a serialized summary +/// type. The opposite of [`FromSummary`]. +pub trait ToSummary { + /// Converts this type into the (usually inferred) input type. + fn to_summary(&self) -> T; +} + +/// Simple and safe conversions from a serialized summary type that may fail in a controlled way +/// under some circumstances. The serialized summary will be stored in the build-metadata, It is the +/// reciprocal of [`ToSummary`]. +pub trait FromSummary: Sized { + /// The type returned in the event of a conversion error. + type Error: Sized; + + /// Performs the conversion. + fn from_summary(summary: T) -> Result; +} + +/// Deprecated in favor of [`BuildPlatformsSummary`]. +impl ToSummary for BuildPlatforms { + fn to_summary(&self) -> PlatformSummary { if let Some(target) = &self.target { - target.platform.to_summary() + target.triple.platform.to_summary() } else { self.host.to_summary() } } } + +impl ToSummary for BuildPlatforms { + fn to_summary(&self) -> HostPlatformSummary { + HostPlatformSummary { + platform: self.host.to_summary(), + } + } +} + +impl ToSummary for BuildPlatformsTarget { + fn to_summary(&self) -> TargetPlatformSummary { + TargetPlatformSummary { + platform: self.triple.platform.to_summary(), + } + } +} + +/// Creates a [`BuildPlatforms`] from a serializable summary for backwards compatibility. +impl FromSummary for BuildPlatforms { + type Error = RustBuildMetaParseError; + + fn from_summary(summary: PlatformSummary) -> Result { + let mut build_platforms = BuildPlatforms::new() + .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?; + if let Some(triple) = TargetTriple::deserialize(Some(summary))? { + build_platforms.target = Some(BuildPlatformsTarget { triple }); + } + Ok(build_platforms) + } +} + +/// Creates a [`BuildPlatforms`] from a serializable summary. +impl FromSummary for BuildPlatforms { + type Error = RustBuildMetaParseError; + + fn from_summary(summary: BuildPlatformsSummary) -> Result { + Ok(BuildPlatforms { + host: summary.host.platform.to_platform()?, + target: { + if summary.targets.len() > 1 { + return Err(RustBuildMetaParseError::Unsupported { + message: "multiple build targets is not supported".to_owned(), + }); + } + let target_platform_summary = summary + .targets + .first() + .map(|target| &target.platform) + .cloned(); + TargetTriple::deserialize(target_platform_summary)? + .map(|triple| BuildPlatformsTarget { triple }) + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_build_platform_new() { + let build_platforms = BuildPlatforms::new().expect("default ctor should succeed"); + assert_eq!( + build_platforms, + BuildPlatforms { + host: Platform::current().expect("should detect the current platform successfully"), + target: None, + } + ); + } +} diff --git a/nextest-runner/src/reporter.rs b/nextest-runner/src/reporter.rs index 9afb258ea64..fe374d7ce99 100644 --- a/nextest-runner/src/reporter.rs +++ b/nextest-runner/src/reporter.rs @@ -2216,7 +2216,7 @@ mod tests { let test_list = TestList::empty(); let config = NextestConfig::default_config("/fake/dir"); let profile = config.profile(NextestConfig::DEFAULT_PROFILE).unwrap(); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let mut buf: Vec = Vec::new(); let output = ReporterStderr::Buffer(&mut buf); diff --git a/nextest-runner/src/runner.rs b/nextest-runner/src/runner.rs index 62c0786602e..6170aae5b59 100644 --- a/nextest-runner/src/runner.rs +++ b/nextest-runner/src/runner.rs @@ -2382,7 +2382,7 @@ mod tests { let test_list = TestList::empty(); let config = NextestConfig::default_config("/fake/dir"); let profile = config.profile(NextestConfig::DEFAULT_PROFILE).unwrap(); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let handler_kind = SignalHandlerKind::Noop; let profile = profile.apply_build_platforms(&build_platforms); let runner = builder diff --git a/nextest-runner/src/target_runner.rs b/nextest-runner/src/target_runner.rs index dcb2fd105f0..4e5e69a1c28 100644 --- a/nextest-runner/src/target_runner.rs +++ b/nextest-runner/src/target_runner.rs @@ -31,7 +31,7 @@ impl TargetRunner { ) -> Result { let host = PlatformRunner::by_precedence(configs, &build_platforms.host)?; let target = match &build_platforms.target { - Some(target) => PlatformRunner::by_precedence(configs, &target.platform)?, + Some(target) => PlatformRunner::by_precedence(configs, &target.triple.platform)?, None => host.clone(), }; diff --git a/nextest-runner/tests/integration/basic.rs b/nextest-runner/tests/integration/basic.rs index 09d0d027a46..71d6d4458a9 100644 --- a/nextest-runner/tests/integration/basic.rs +++ b/nextest-runner/tests/integration/basic.rs @@ -27,7 +27,7 @@ fn test_list_binaries() -> Result<()> { set_env_vars(); let graph = &*PACKAGE_GRAPH; - let build_platforms = BuildPlatforms::new(None)?; + let build_platforms = BuildPlatforms::new()?; let binary_list = BinaryList::from_messages( Cursor::new(&*FIXTURE_RAW_CARGO_TEST_OUTPUT), graph, @@ -104,7 +104,7 @@ fn test_run() -> Result<()> { let profile = config .profile(NextestConfig::DEFAULT_PROFILE) .expect("default config is valid"); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let profile = profile.apply_build_platforms(&build_platforms); let runner = TestRunnerBuilder::default() @@ -213,7 +213,7 @@ fn test_run_ignored() -> Result<()> { let profile = config .profile(NextestConfig::DEFAULT_PROFILE) .expect("default config is valid"); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let profile = profile.apply_build_platforms(&build_platforms); let runner = TestRunnerBuilder::default() @@ -411,7 +411,7 @@ fn test_retries(retries: Option) -> Result<()> { let profile = config .profile("with-retries") .expect("with-retries config is valid"); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let profile = profile.apply_build_platforms(&build_platforms); let profile_retries = profile.retries(); @@ -561,7 +561,7 @@ fn test_termination() -> Result<()> { let profile = config .profile("with-termination") .expect("with-termination config is valid"); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let profile = profile.apply_build_platforms(&build_platforms); let runner = TestRunnerBuilder::default() diff --git a/nextest-runner/tests/integration/fixtures.rs b/nextest-runner/tests/integration/fixtures.rs index ff480855c03..5b4097d4a90 100644 --- a/nextest-runner/tests/integration/fixtures.rs +++ b/nextest-runner/tests/integration/fixtures.rs @@ -332,7 +332,7 @@ impl FixtureTargets { ) .unwrap(); let env = EnvironmentMap::new(&cargo_configs); - let build_platforms = BuildPlatforms::new(None).unwrap(); + let build_platforms = BuildPlatforms::new().unwrap(); let binary_list = Arc::new( BinaryList::from_messages( Cursor::new(&*FIXTURE_RAW_CARGO_TEST_OUTPUT), diff --git a/nextest-runner/tests/integration/target_runner.rs b/nextest-runner/tests/integration/target_runner.rs index 11e7e3ca1d1..6e3245c9fbd 100644 --- a/nextest-runner/tests/integration/target_runner.rs +++ b/nextest-runner/tests/integration/target_runner.rs @@ -8,7 +8,7 @@ use nextest_runner::{ cargo_config::{CargoConfigs, TargetTriple}, config::NextestConfig, double_spawn::DoubleSpawnInfo, - platform::BuildPlatforms, + platform::{BuildPlatforms, BuildPlatformsTarget}, runner::TestRunnerBuilder, signal::SignalHandlerKind, target_runner::{PlatformRunner, TargetRunner}, @@ -25,8 +25,10 @@ fn runner_for_target(triple: Option<&str>) -> Result<(BuildPlatforms, TargetRunn Vec::new(), ) .unwrap(); - let triple = TargetTriple::find(&configs, triple)?; - let build_platforms = BuildPlatforms::new(triple)?; + let mut build_platforms = BuildPlatforms::new()?; + if let Some(triple) = TargetTriple::find(&configs, triple)? { + build_platforms.target = Some(BuildPlatformsTarget { triple }); + } let target_runner = TargetRunner::new(&configs, &build_platforms)?; Ok((build_platforms, target_runner)) } From 5fc24e098b72f7322cf7d7d715dce6862bce6040 Mon Sep 17 00:00:00 2001 From: Kaiyi Li Date: Wed, 15 May 2024 07:58:45 -0700 Subject: [PATCH 3/5] Add RustcCli ... which stream lines the call to the `rustc` binary. `RustcCli` will be used to decide the host and target libdir in a following commit. --- Cargo.lock | 1 + cargo-nextest/Cargo.toml | 3 + nextest-runner/Cargo.toml | 2 +- nextest-runner/src/lib.rs | 6 ++ nextest-runner/src/rustc_cli.rs | 145 ++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 nextest-runner/src/rustc_cli.rs diff --git a/Cargo.lock b/Cargo.lock index c4373ff6b03..274b17f920d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,7 @@ name = "cargo-nextest" version = "0.9.70" dependencies = [ "camino", + "camino-tempfile", "cfg-if", "clap", "color-eyre", diff --git a/cargo-nextest/Cargo.toml b/cargo-nextest/Cargo.toml index 3e7098bbd48..7308d19a9eb 100644 --- a/cargo-nextest/Cargo.toml +++ b/cargo-nextest/Cargo.toml @@ -41,6 +41,9 @@ swrite.workspace = true thiserror = "1.0.60" nextest-workspace-hack.workspace = true +[dev-dependencies] +camino-tempfile = "1.1.1" + [features] default = ["default-no-update", "self-update"] experimental-tokio-console = ["nextest-runner/experimental-tokio-console"] diff --git a/nextest-runner/Cargo.toml b/nextest-runner/Cargo.toml index f4d73b67afb..0f61fb16aeb 100644 --- a/nextest-runner/Cargo.toml +++ b/nextest-runner/Cargo.toml @@ -31,6 +31,7 @@ cfg-if = "1.0.0" chrono = "0.4.38" debug-ignore.workspace = true display-error-chain = "0.2.0" +duct = "0.13.7" either = "1.11.0" futures = "0.3.30" guppy = "0.17.5" @@ -130,7 +131,6 @@ self_update = { version = "0.39.0", optional = true } [dev-dependencies] color-eyre = { version = "0.6.3", default-features = false } -duct = "0.13.7" indoc = "2.0.5" insta = { version = "1.39.0", default-features = false } maplit = "1.0.2" diff --git a/nextest-runner/src/lib.rs b/nextest-runner/src/lib.rs index 5505e31e76e..f364ae567a8 100644 --- a/nextest-runner/src/lib.rs +++ b/nextest-runner/src/lib.rs @@ -24,6 +24,10 @@ pub mod redact; pub mod reporter; pub mod reuse_build; pub mod runner; +// TODO: move this module to the cargo-nextest crate and make it a private module once we get rid of +// the tests in nextest-runner/tests/integration which depend on this to provide correct host and +// target libdir. +mod rustc_cli; pub mod show_config; pub mod signal; pub mod target_runner; @@ -34,3 +38,5 @@ mod time; #[cfg(feature = "self-update")] pub mod update; pub mod write_str; + +pub use rustc_cli::RustcCli; diff --git a/nextest-runner/src/rustc_cli.rs b/nextest-runner/src/rustc_cli.rs new file mode 100644 index 00000000000..40ceec984af --- /dev/null +++ b/nextest-runner/src/rustc_cli.rs @@ -0,0 +1,145 @@ +// Copyright (c) The nextest Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::cargo_config::TargetTriple; +use camino::Utf8PathBuf; +use std::{borrow::Cow, path::PathBuf}; + +/// Create a rustc CLI call. +#[derive(Clone, Debug)] +pub struct RustcCli<'a> { + rustc_path: Utf8PathBuf, + args: Vec>, +} + +impl<'a> RustcCli<'a> { + /// Create a rustc CLI call: `rustc --print target-libdir`. + pub fn print_host_libdir() -> Self { + let mut cli = Self::default(); + cli.add_arg("--print").add_arg("target-libdir"); + cli + } + + /// Create a rustc CLI call: `rustc --print target-libdir --target `. + pub fn print_target_libdir(triple: &'a TargetTriple) -> Self { + let mut cli = Self::default(); + cli.add_arg("--print") + .add_arg("target-libdir") + .add_arg("--target") + .add_arg(triple.platform.triple_str()); + cli + } + + fn add_arg(&mut self, arg: impl Into>) -> &mut Self { + self.args.push(arg.into()); + self + } + + fn to_expression(&self) -> duct::Expression { + duct::cmd( + self.rustc_path.as_str(), + self.args.iter().map(|arg| arg.as_ref()), + ) + } + + /// Execute the command, capture its standard output, and return the captured output as a + /// [`Vec`]. + pub fn read(&self) -> Option> { + let expression = self.to_expression(); + log::trace!("Executing command: {:?}", expression); + let output = match expression + .stdout_capture() + .stderr_capture() + .unchecked() + .run() + { + Ok(output) => output, + Err(e) => { + log::debug!("Failed to spawn the child process: {}", e); + return None; + } + }; + if !output.status.success() { + log::debug!("The execution of the command failed with {}", output.status); + log::debug!("stdout:"); + log::debug!("{}", String::from_utf8_lossy(&output.stdout)); + log::debug!("stderr:"); + log::debug!("{}", String::from_utf8_lossy(&output.stderr)); + return None; + } + Some(output.stdout) + } +} + +impl<'a> Default for RustcCli<'a> { + fn default() -> Self { + Self { + rustc_path: rustc_path(), + args: vec![], + } + } +} + +fn rustc_path() -> Utf8PathBuf { + match std::env::var_os("RUSTC") { + Some(rustc_path) => PathBuf::from(rustc_path) + .try_into() + .expect("RUSTC env var is not valid UTF-8"), + None => Utf8PathBuf::from("rustc"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use camino_tempfile::Utf8TempDir; + use std::env; + + #[test] + fn test_should_run_rustc_version() { + let mut cli = RustcCli::default(); + cli.add_arg("--version"); + let output = cli.read().expect("rustc --version should run successfully"); + let output = String::from_utf8(output).expect("the output should be valid utf-8"); + assert!( + output.starts_with("rustc"), + "The output should start with rustc, but the actual output is: {}", + output + ); + } + + #[test] + fn test_should_respect_rustc_env() { + env::set_var("RUSTC", "cargo"); + let mut cli = RustcCli::default(); + cli.add_arg("--version"); + let output = cli.read().expect("cargo --version should run successfully"); + let output = String::from_utf8(output).expect("the output should be valid utf-8"); + assert!( + output.starts_with("cargo"), + "The output should start with cargo, but the actual output is: {}", + output + ); + } + + #[test] + fn test_fail_to_spawn() { + let fake_dir = Utf8TempDir::new().expect("should create the temp dir successfully"); + // No OS will allow executing a directory. + env::set_var("RUSTC", fake_dir.path()); + let mut cli = RustcCli::default(); + cli.add_arg("--version"); + let output = cli.read(); + assert_eq!(output, None); + } + + #[test] + fn test_execute_with_failure() { + let mut cli = RustcCli::default(); + // rustc --print Y7uDG1HrrY should fail + cli.add_arg("--print"); + cli.add_arg("Y7uDG1HrrY"); + let output = cli.read(); + assert_eq!(output, None); + } +} From c83105f3b35721379f8f32da9643de3c872ecb0f Mon Sep 17 00:00:00 2001 From: Kaiyi Li Date: Sun, 12 May 2024 23:51:23 -0700 Subject: [PATCH 4/5] Add rustc libdir folders to dynamic linker search path ... so that we can run cargo-nextest with proc-macro projects on Windows. This commit also adds integration tests that run cargo-nextest with a proc-macro project. --- Cargo.lock | 1 + cargo-nextest/src/dispatch.rs | 10 +- fixtures/nextest-tests/Cargo.lock | 4 + fixtures/nextest-tests/Cargo.toml | 1 + .../nextest-tests/proc-macro-test/Cargo.toml | 7 + .../nextest-tests/proc-macro-test/src/lib.rs | 0 integration-tests/Cargo.toml | 1 + .../tests/integration/fixtures.rs | 16 +- integration-tests/tests/integration/main.rs | 23 ++- .../integration__archive_includes.snap | 2 +- ...gration__archive_includes_without_uds.snap | 2 +- ...integration__archive_missing_includes.snap | 2 +- .../integration__archive_no_includes.snap | 2 +- nextest-metadata/src/test_list.rs | 12 ++ nextest-runner/src/config/test_helpers.rs | 6 + nextest-runner/src/list/binary_list.rs | 10 +- nextest-runner/src/list/rust_build_meta.rs | 126 +++++++++++++- nextest-runner/src/list/test_list.rs | 10 +- nextest-runner/src/platform.rs | 161 +++++++++++++++++- nextest-runner/tests/integration/fixtures.rs | 1 + .../tests/integration/target_runner.rs | 12 +- 21 files changed, 379 insertions(+), 30 deletions(-) create mode 100644 fixtures/nextest-tests/proc-macro-test/Cargo.toml create mode 100644 fixtures/nextest-tests/proc-macro-test/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 274b17f920d..b91d0e981ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1373,6 +1373,7 @@ dependencies = [ "pathdiff", "regex", "serde_json", + "target-spec", ] [[package]] diff --git a/cargo-nextest/src/dispatch.rs b/cargo-nextest/src/dispatch.rs index 1570c8a33a8..6d3e9468db7 100644 --- a/cargo-nextest/src/dispatch.rs +++ b/cargo-nextest/src/dispatch.rs @@ -37,6 +37,7 @@ use nextest_runner::{ target_runner::{PlatformRunner, TargetRunner}, test_filter::{RunIgnored, TestFilterBuilder}, write_str::WriteStr, + RustcCli, }; use once_cell::sync::OnceCell; use owo_colors::{OwoColorize, Stream, Style}; @@ -1009,10 +1010,17 @@ impl BaseApp { Some(kind) => kind.binary_list.rust_build_meta.build_platforms.clone(), None => { let mut build_platforms = BuildPlatforms::new()?; + if let Some(output) = RustcCli::print_host_libdir().read() { + build_platforms.set_host_libdir_from_rustc_output(Cursor::new(output)); + } if let Some(triple) = discover_target_triple(&cargo_configs, cargo_opts.target.as_deref()) { - build_platforms.target = Some(BuildPlatformsTarget { triple }); + let mut target = BuildPlatformsTarget::new(triple.clone()); + if let Some(output) = RustcCli::print_target_libdir(&triple).read() { + target.set_libdir_from_rustc_output(Cursor::new(output)); + } + build_platforms.target = Some(target); } build_platforms } diff --git a/fixtures/nextest-tests/Cargo.lock b/fixtures/nextest-tests/Cargo.lock index 789d4353e4c..3dff4e96d55 100644 --- a/fixtures/nextest-tests/Cargo.lock +++ b/fixtures/nextest-tests/Cargo.lock @@ -26,6 +26,10 @@ dependencies = [ "uuid", ] +[[package]] +name = "proc-macro-test" +version = "0.1.0" + [[package]] name = "uuid" version = "1.2.1" diff --git a/fixtures/nextest-tests/Cargo.toml b/fixtures/nextest-tests/Cargo.toml index 93394f96357..8e5a299923f 100644 --- a/fixtures/nextest-tests/Cargo.toml +++ b/fixtures/nextest-tests/Cargo.toml @@ -34,6 +34,7 @@ members = [ "derive", "dylib-test", "with-build-script", + "proc-macro-test" ] [dependencies] diff --git a/fixtures/nextest-tests/proc-macro-test/Cargo.toml b/fixtures/nextest-tests/proc-macro-test/Cargo.toml new file mode 100644 index 00000000000..4ba8c79e372 --- /dev/null +++ b/fixtures/nextest-tests/proc-macro-test/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "proc-macro-test" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true diff --git a/fixtures/nextest-tests/proc-macro-test/src/lib.rs b/fixtures/nextest-tests/proc-macro-test/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index b311a4b8900..075d08c20d5 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -36,3 +36,4 @@ once_cell = "1.19.0" regex = "1.10.4" serde_json = "1.0.117" insta = { version = "1.39.0", default-features = false } +target-spec = { version = "3.1.0", features = ["custom", "summaries"] } diff --git a/integration-tests/tests/integration/fixtures.rs b/integration-tests/tests/integration/fixtures.rs index e3af588973e..76574971255 100644 --- a/integration-tests/tests/integration/fixtures.rs +++ b/integration-tests/tests/integration/fixtures.rs @@ -128,6 +128,7 @@ pub static EXPECTED_LIST: Lazy> = Lazy::new(|| { BuildPlatform::Target, vec![("tests::test_out_dir_present", false)], ), + TestInfo::new("proc-macro-test", BuildPlatform::Host, vec![]), ] }); @@ -379,7 +380,20 @@ pub fn check_list_binaries_output(stdout: &[u8]) { let result: BinaryListSummary = serde_json::from_slice(stdout).unwrap(); let test_suite = &*EXPECTED_LIST; - assert_eq!(test_suite.len(), result.rust_binaries.len()); + let mut expected_binary_ids = test_suite + .iter() + .map(|test_info| test_info.id.clone()) + .collect::>(); + expected_binary_ids.sort(); + let mut actual_binary_ids = result.rust_binaries.keys().collect::>(); + actual_binary_ids.sort(); + assert_eq!( + test_suite.len(), + result.rust_binaries.len(), + "expected rust binaries:\n{:?}\nactual rust binaries\n{:?}", + expected_binary_ids, + actual_binary_ids + ); for test in test_suite { let entry = result diff --git a/integration-tests/tests/integration/main.rs b/integration-tests/tests/integration/main.rs index cd6ef370c38..6a2dcbca539 100644 --- a/integration-tests/tests/integration/main.rs +++ b/integration-tests/tests/integration/main.rs @@ -23,8 +23,9 @@ //! `NEXTEST_BIN_EXE_cargo-nextest-dup`. use camino::{Utf8Path, Utf8PathBuf}; -use nextest_metadata::{BuildPlatform, NextestExitCode}; +use nextest_metadata::{BuildPlatform, NextestExitCode, TestListSummary}; use std::{fs::File, io::Write}; +use target_spec::Platform; mod fixtures; mod temp_project; @@ -818,3 +819,23 @@ fn test_setup_script_error() { Some(NextestExitCode::SETUP_SCRIPT_FAILED) ); } + +#[test] +fn test_target_arg() { + let host_platform = Platform::current().expect("should detect the host target successfully"); + let host_triple = host_platform.triple_str(); + let output = CargoNextestCli::new() + .args(["list", "--target", host_triple, "--message-format", "json"]) + .output(); + let result: TestListSummary = serde_json::from_slice(&output.stdout).unwrap(); + let build_platforms = &result + .rust_build_meta + .platforms + .expect("should have the platforms field"); + assert_eq!(build_platforms.host.platform, host_platform.to_summary()); + assert_eq!(build_platforms.targets[0].platform.triple, host_triple); + assert_eq!( + build_platforms.targets[0].libdir, + build_platforms.host.libdir + ); +} diff --git a/integration-tests/tests/integration/snapshots/integration__archive_includes.snap b/integration-tests/tests/integration/snapshots/integration__archive_includes.snap index 0d1bb2179fa..031c8ec4727 100644 --- a/integration-tests/tests/integration/snapshots/integration__archive_includes.snap +++ b/integration-tests/tests/integration/snapshots/integration__archive_includes.snap @@ -2,7 +2,7 @@ source: integration-tests/tests/integration/main.rs expression: output.stderr_as_str() --- - Archiving 16 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 7 extra paths to + Archiving 17 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 7 extra paths to Warning ignoring extra path `/excluded-dir` because it does not exist Warning ignoring extra path `/depth-0-dir` specified with depth 0 since it is a directory Warning ignoring extra path `/file_that_does_not_exist.txt` because it does not exist diff --git a/integration-tests/tests/integration/snapshots/integration__archive_includes_without_uds.snap b/integration-tests/tests/integration/snapshots/integration__archive_includes_without_uds.snap index 17fb0bb6b42..1207aced2c2 100644 --- a/integration-tests/tests/integration/snapshots/integration__archive_includes_without_uds.snap +++ b/integration-tests/tests/integration/snapshots/integration__archive_includes_without_uds.snap @@ -2,7 +2,7 @@ source: integration-tests/tests/integration/main.rs expression: output.stderr_as_str() --- - Archiving 16 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 7 extra paths to + Archiving 17 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 7 extra paths to Warning ignoring extra path `/excluded-dir` because it does not exist Warning ignoring extra path `/depth-0-dir` specified with depth 0 since it is a directory Warning ignoring extra path `/file_that_does_not_exist.txt` because it does not exist diff --git a/integration-tests/tests/integration/snapshots/integration__archive_missing_includes.snap b/integration-tests/tests/integration/snapshots/integration__archive_missing_includes.snap index 32b71d128af..216ad0a2bc3 100644 --- a/integration-tests/tests/integration/snapshots/integration__archive_missing_includes.snap +++ b/integration-tests/tests/integration/snapshots/integration__archive_missing_includes.snap @@ -2,7 +2,7 @@ source: integration-tests/tests/integration/main.rs expression: output.stderr_as_str() --- - Archiving 16 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 1 extra path to + Archiving 17 binaries (including 2 non-test binaries), 2 build script output directories, 2 linked paths, and 1 extra path to error: error creating archive `` Caused by: diff --git a/integration-tests/tests/integration/snapshots/integration__archive_no_includes.snap b/integration-tests/tests/integration/snapshots/integration__archive_no_includes.snap index 4eb2ace1975..5b7e571f30b 100644 --- a/integration-tests/tests/integration/snapshots/integration__archive_no_includes.snap +++ b/integration-tests/tests/integration/snapshots/integration__archive_no_includes.snap @@ -2,7 +2,7 @@ source: integration-tests/tests/integration/main.rs expression: output.stderr_as_str() --- - Archiving 16 binaries (including 2 non-test binaries), 2 build script output directories, and 2 linked paths to + Archiving 17 binaries (including 2 non-test binaries), 2 build script output directories, and 2 linked paths to Warning linked path `/debug/build//does-not-exist` not found, requested by: cdylib-link v0.1.0 (this is a bug in this crate that should be fixed) Archived files to in diff --git a/nextest-metadata/src/test_list.rs b/nextest-metadata/src/test_list.rs index 844bbd84c88..10230a02e55 100644 --- a/nextest-metadata/src/test_list.rs +++ b/nextest-metadata/src/test_list.rs @@ -522,6 +522,12 @@ pub struct RustNonTestBinarySummary { pub struct HostPlatformSummary { /// The host platform, if specified. pub platform: PlatformSummary, + + /// The libdir for the host platform. + /// + /// Empty if failed to discover. + #[serde(default)] + pub libdir: Option, } /// Serialized representation of the target platform. @@ -530,6 +536,12 @@ pub struct HostPlatformSummary { pub struct TargetPlatformSummary { /// The target platform, if specified. pub platform: PlatformSummary, + + /// The libdir for the target platform. + /// + /// Empty if failed to discover. + #[serde(default)] + pub libdir: Option, } /// Serialized representation of the host and the target platform. diff --git a/nextest-runner/src/config/test_helpers.rs b/nextest-runner/src/config/test_helpers.rs index 8bd100c3f85..e88992949c6 100644 --- a/nextest-runner/src/config/test_helpers.rs +++ b/nextest-runner/src/config/test_helpers.rs @@ -86,12 +86,18 @@ pub(super) fn binary_query<'a>( pub(super) fn build_platforms() -> BuildPlatforms { BuildPlatforms { host: Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(), + host_libdir: Some( + Utf8PathBuf::from("/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib") + ), target: Some(BuildPlatformsTarget { triple: TargetTriple { platform: Platform::new("aarch64-apple-darwin", TargetFeatures::Unknown).unwrap(), source: TargetTripleSource::Env, location: TargetDefinitionLocation::Builtin, }, + libdir: Some( + Utf8PathBuf::from("/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-apple-darwin/lib") + ), }), } } diff --git a/nextest-runner/src/list/binary_list.rs b/nextest-runner/src/list/binary_list.rs index 5c4924bb8a5..482d97c9abc 100644 --- a/nextest-runner/src/list/binary_list.rs +++ b/nextest-runner/src/list/binary_list.rs @@ -427,10 +427,14 @@ mod tests { source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; + let fake_host_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"; + let fake_target_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib"; let build_platforms = BuildPlatforms { host: TargetTriple::x86_64_unknown_linux_gnu().platform, + host_libdir: Some(Utf8PathBuf::from(fake_host_libdir)), target: Some(BuildPlatformsTarget { triple: fake_triple, + libdir: Some(Utf8PathBuf::from(fake_target_libdir)), }), }; @@ -510,14 +514,16 @@ mod tests { "platform": { "triple": "x86_64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" }, "targets": [ { "platform": { "triple": "aarch64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib" } ] }, diff --git a/nextest-runner/src/list/rust_build_meta.rs b/nextest-runner/src/list/rust_build_meta.rs index e2b4df8a153..d7bdbfce6b2 100644 --- a/nextest-runner/src/list/rust_build_meta.rs +++ b/nextest-runner/src/list/rust_build_meta.rs @@ -9,6 +9,7 @@ use crate::{ reuse_build::PathMapper, }; use camino::Utf8PathBuf; +use itertools::Itertools; use nextest_metadata::{BuildPlatformsSummary, RustBuildMetaSummary, RustNonTestBinarySummary}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -83,8 +84,6 @@ impl RustBuildMeta { /// Empty metadata for tests. #[cfg(test)] pub(crate) fn empty() -> Self { - use target_spec::Platform; - Self { target_directory: Utf8PathBuf::new(), base_output_directories: BTreeSet::new(), @@ -92,10 +91,7 @@ impl RustBuildMeta { build_script_out_dirs: BTreeMap::new(), linked_paths: BTreeMap::new(), state: PhantomData, - build_platforms: BuildPlatforms { - host: Platform::current().unwrap(), - target: None, - }, + build_platforms: BuildPlatforms::new().unwrap(), } } @@ -109,6 +105,21 @@ impl RustBuildMeta { // FIXME/HELP WANTED: get the rustc sysroot library path here. // See https://github.com/nextest-rs/nextest/issues/267. + let libdirs = self + .build_platforms + .host_libdir + .iter() + .chain( + self.build_platforms + .target + .as_ref() + .and_then(|target| target.libdir.as_ref()), + ) + .cloned() + .collect::>(); + if libdirs.is_empty() { + log::warn!("failed to detect the rustc libdir, may fail to list or run tests"); + } // Cargo puts linked paths before base output directories. self.linked_paths .keys() @@ -127,6 +138,10 @@ impl RustBuildMeta { // This is the order paths are added in by Cargo. [with_deps, abs_base] })) + // Add the rustc libdir paths to the search paths to run procudure macro binaries. See + // details in https://github.com/nextest-rs/nextest/issues/1493. + .chain(libdirs) + .unique() .collect() } } @@ -233,6 +248,7 @@ mod tests { build_platforms: BuildPlatforms { host: host_platform(), target: None, + host_libdir: None, }, ..Default::default() }; "no target platforms")] @@ -242,8 +258,10 @@ mod tests { }, RustBuildMeta:: { build_platforms: BuildPlatforms { host: host_platform(), + host_libdir: None, target: Some(BuildPlatformsTarget{ triple: TargetTriple::x86_64_unknown_linux_gnu(), + libdir: None, }), }, ..Default::default() @@ -255,8 +273,10 @@ mod tests { }, RustBuildMeta:: { build_platforms: BuildPlatforms { host: host_platform(), + host_libdir: None, target: Some(BuildPlatformsTarget{ - triple: x86_64_pc_windows_msvc_triple() + triple: x86_64_pc_windows_msvc_triple(), + libdir: None, }), }, ..Default::default() @@ -267,17 +287,21 @@ mod tests { platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { platform: not_host_platform_triple().platform.to_summary(), + libdir: Some("/fake/test/libdir/281".into()), }, targets: vec![TargetPlatformSummary { platform: PlatformSummary::new("aarch64-unknown-linux-gnu"), + libdir: Some("/fake/test/libdir/837".into()), }], }), ..Default::default() }, RustBuildMeta:: { build_platforms: BuildPlatforms { host: not_host_platform_triple().platform, + host_libdir: Some("/fake/test/libdir/281".into()), target: Some(BuildPlatformsTarget{ triple: aarch64_unknown_linux_gnu_triple(), + libdir: Some("/fake/test/libdir/837".into()), }), }, ..Default::default() @@ -286,6 +310,7 @@ mod tests { platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { platform: PlatformSummary::new("x86_64-apple-darwin"), + libdir: None, }, targets: vec![], }), @@ -293,6 +318,7 @@ mod tests { }, RustBuildMeta:: { build_platforms: BuildPlatforms { host: x86_64_apple_darwin_triple().platform, + host_libdir: None, target: None, }, ..Default::default() @@ -309,13 +335,16 @@ mod tests { platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { platform: PlatformSummary::new("x86_64-apple-darwin"), + libdir: None, }, targets: vec![ TargetPlatformSummary { platform: PlatformSummary::new("aarch64-unknown-linux-gnu"), + libdir: None, }, TargetPlatformSummary { platform: PlatformSummary::new("x86_64-pc-windows-msvc"), + libdir: None, }, ], }), @@ -331,6 +360,7 @@ mod tests { platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { platform: PlatformSummary::new("invalid-platform-triple"), + libdir: None, }, targets: vec![], }), @@ -358,6 +388,7 @@ mod tests { build_platforms: BuildPlatforms { host: host_platform(), target: None, + host_libdir: None, }, ..Default::default() }, RustBuildMetaSummary { @@ -365,7 +396,8 @@ mod tests { target_platforms: vec![host_platform().to_summary()], platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { - platform: host_platform().to_summary() + platform: host_platform().to_summary(), + libdir: None, }, targets: vec![], }), @@ -374,8 +406,10 @@ mod tests { #[test_case(RustBuildMeta:: { build_platforms: BuildPlatforms { host: host_platform(), + host_libdir: Some("/fake/test/libdir/736".into()), target: Some(BuildPlatformsTarget { - triple: not_host_platform_triple() + triple: not_host_platform_triple(), + libdir: Some(Utf8PathBuf::from("/fake/test/libdir/873")), }), }, ..Default::default() @@ -385,9 +419,11 @@ mod tests { platforms: Some(BuildPlatformsSummary { host: HostPlatformSummary { platform: host_platform().to_summary(), + libdir: Some("/fake/test/libdir/736".into()), }, targets: vec![TargetPlatformSummary { platform: not_host_platform_triple().platform.to_summary(), + libdir: Some("/fake/test/libdir/873".into()), }], }), ..Default::default() @@ -396,4 +432,76 @@ mod tests { let actual = meta.to_summary(); assert_eq!(actual, expected); } + + #[test] + fn test_dylib_paths_should_include_rustc_dir() { + let host_libdir = Utf8PathBuf::from("/fake/rustc/host/libdir"); + let target_libdir = Utf8PathBuf::from("/fake/rustc/target/libdir"); + + let rust_build_meta = RustBuildMeta { + build_platforms: { + let mut build_platforms = BuildPlatforms::new() + .expect("Should create BuildPlatforms with default ctor successfully"); + build_platforms.host_libdir = Some(host_libdir.clone()); + let mut target = + BuildPlatformsTarget::new(TargetTriple::x86_64_unknown_linux_gnu()); + target.libdir = Some(target_libdir.clone()); + build_platforms.target = Some(target); + build_platforms + }, + ..RustBuildMeta::empty() + }; + let dylib_paths = rust_build_meta.dylib_paths(); + + assert!( + dylib_paths.contains(&host_libdir), + "{:?} should contain {}", + dylib_paths, + host_libdir + ); + assert!( + dylib_paths.contains(&target_libdir), + "{:?} should contain {}", + dylib_paths, + target_libdir + ); + } + + #[test] + fn test_dylib_paths_should_not_contain_duplicate_paths() { + let tmpdir = camino_tempfile::tempdir().expect("should create temp dir successfully"); + let host_libdir = tmpdir.path().to_path_buf(); + let target_libdir = host_libdir.clone(); + let fake_target_dir = tmpdir + .path() + .parent() + .expect("tmp directory should have a parent"); + let tmpdir_dirname = tmpdir + .path() + .file_name() + .expect("tmp directory should have a file name"); + + let rust_build_meta = RustBuildMeta { + target_directory: fake_target_dir.to_path_buf(), + linked_paths: [(Utf8PathBuf::from(tmpdir_dirname), Default::default())].into(), + base_output_directories: [Utf8PathBuf::from(tmpdir_dirname)].into(), + build_platforms: { + let mut build_platforms = BuildPlatforms::new() + .expect("should create BuildPlatforms with default ctor successfully"); + let mut target = + BuildPlatformsTarget::new(TargetTriple::x86_64_unknown_linux_gnu()); + target.libdir = Some(target_libdir.clone()); + build_platforms.target = Some(target); + build_platforms + }, + ..RustBuildMeta::empty() + }; + let dylib_paths = rust_build_meta.dylib_paths(); + + assert!( + dylib_paths.clone().into_iter().all_unique(), + "{:?} should not contain duplicate paths", + dylib_paths + ); + } } diff --git a/nextest-runner/src/list/test_list.rs b/nextest-runner/src/list/test_list.rs index 183ee1f86b2..48cdd2ecc45 100644 --- a/nextest-runner/src/list/test_list.rs +++ b/nextest-runner/src/list/test_list.rs @@ -1033,10 +1033,14 @@ mod tests { source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; + let fake_host_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"; + let fake_target_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib"; let build_platforms = BuildPlatforms { host: TargetTriple::x86_64_unknown_linux_gnu().platform, + host_libdir: Some(fake_host_libdir.into()), target: Some(BuildPlatformsTarget { triple: fake_triple, + libdir: Some(fake_target_libdir.into()), }), }; @@ -1150,14 +1154,16 @@ mod tests { "platform": { "triple": "x86_64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" }, "targets": [ { "platform": { "triple": "aarch64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib" } ] }, diff --git a/nextest-runner/src/platform.rs b/nextest-runner/src/platform.rs index 7ea0635ffe0..d8feae5b145 100644 --- a/nextest-runner/src/platform.rs +++ b/nextest-runner/src/platform.rs @@ -7,15 +7,62 @@ use crate::{ cargo_config::{CargoTargetArg, TargetTriple}, errors::{RustBuildMetaParseError, TargetTripleError, UnknownHostPlatform}, }; +use camino::Utf8PathBuf; use nextest_metadata::{BuildPlatformsSummary, HostPlatformSummary, TargetPlatformSummary}; +use std::io; use target_spec::summaries::PlatformSummary; pub use target_spec::Platform; +fn read_first_line_as_path(reader: impl io::BufRead) -> Option { + // We will print warn logs later when we are adding the path to the dynamic linker search paths, + // so we don't print the warn log here to avoid spammy log. + match reader.lines().next() { + Some(Ok(line)) => { + let original_line = line.as_str(); + let line = line.trim(); + if line.is_empty() { + log::debug!("empty input found: {:#?}", original_line); + return None; + } + Some(Utf8PathBuf::from(line)) + } + Some(Err(e)) => { + log::debug!("failed to read the input: {:#?}", e); + None + } + None => { + log::debug!("empty input"); + None + } + } +} + /// The target platform. #[derive(Clone, Debug, Eq, PartialEq)] pub struct BuildPlatformsTarget { /// The target triplet, which consists of machine, vendor and OS. pub triple: TargetTriple, + + /// The target libdir. + pub libdir: Option, +} + +impl BuildPlatformsTarget { + /// Creates a new [`BuildPlatformsTarget`] and set the [`Self::triple`] to the imput `triple`. + pub fn new(triple: TargetTriple) -> Self { + Self { + triple, + libdir: None, + } + } + + /// Try to parse the rustc output and set [`Self::libdir`]. If the we fail to parse the input + /// [`Self::libdir`] will be set to [`None`]. + /// + /// Used to set the dynamic linker search path when running the test executables. + pub fn set_libdir_from_rustc_output(&mut self, reader: impl io::BufRead) { + self.libdir = read_first_line_as_path(reader); + } } /// A representation of host and target platform. @@ -24,6 +71,9 @@ pub struct BuildPlatforms { /// The host platform. pub host: Platform, + /// The host libdir. + pub host_libdir: Option, + /// The target platform, if specified. pub target: Option, } @@ -34,7 +84,19 @@ impl BuildPlatforms { /// Returns an error if the host platform could not be determined. pub fn new() -> Result { let host = Platform::current().map_err(|error| UnknownHostPlatform { error })?; - Ok(Self { host, target: None }) + Ok(Self { + host, + host_libdir: None, + target: None, + }) + } + + /// Try to parse the rustc output and set [`Self::host_libdir`]. If the we fail to parse the + /// input [`Self::host_libdir`] will be set to [`None`]. + /// + /// Used to set the dynamic linker search path when running the test executables. + pub fn set_host_libdir_from_rustc_output(&mut self, reader: impl io::BufRead) { + self.host_libdir = read_first_line_as_path(reader); } /// Returns the argument to pass into `cargo metadata --filter-platform `. @@ -64,7 +126,7 @@ impl BuildPlatforms { let mut build_platforms = BuildPlatforms::new() .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?; if let Some(triple) = TargetTriple::deserialize_str(summary)? { - build_platforms.target = Some(BuildPlatformsTarget { triple }); + build_platforms.target = Some(BuildPlatformsTarget::new(triple)); } Ok(build_platforms) } @@ -103,6 +165,7 @@ impl ToSummary for BuildPlatforms { fn to_summary(&self) -> HostPlatformSummary { HostPlatformSummary { platform: self.host.to_summary(), + libdir: self.host_libdir.clone(), } } } @@ -111,6 +174,7 @@ impl ToSummary for BuildPlatformsTarget { fn to_summary(&self) -> TargetPlatformSummary { TargetPlatformSummary { platform: self.triple.platform.to_summary(), + libdir: self.libdir.clone(), } } } @@ -123,7 +187,7 @@ impl FromSummary for BuildPlatforms { let mut build_platforms = BuildPlatforms::new() .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error))?; if let Some(triple) = TargetTriple::deserialize(Some(summary))? { - build_platforms.target = Some(BuildPlatformsTarget { triple }); + build_platforms.target = Some(BuildPlatformsTarget::new(triple)); } Ok(build_platforms) } @@ -136,19 +200,24 @@ impl FromSummary for BuildPlatforms { fn from_summary(summary: BuildPlatformsSummary) -> Result { Ok(BuildPlatforms { host: summary.host.platform.to_platform()?, + host_libdir: summary.host.libdir, target: { if summary.targets.len() > 1 { return Err(RustBuildMetaParseError::Unsupported { message: "multiple build targets is not supported".to_owned(), }); } - let target_platform_summary = summary + summary .targets .first() - .map(|target| &target.platform) - .cloned(); - TargetTriple::deserialize(target_platform_summary)? - .map(|triple| BuildPlatformsTarget { triple }) + .map(|target| { + Ok::<_, Self::Error>(BuildPlatformsTarget { + triple: TargetTriple::deserialize(Some(target.platform.clone()))? + .expect("the input is not None, so the output must not be None"), + libdir: target.libdir.clone(), + }) + }) + .transpose()? }, }) } @@ -157,6 +226,68 @@ impl FromSummary for BuildPlatforms { #[cfg(test)] mod tests { use super::*; + use indoc::indoc; + use std::io::Cursor; + use test_case::test_case; + + #[test] + fn test_read_from_rustc_output_failed() { + struct ReadMock; + impl io::Read for ReadMock { + fn read(&mut self, _: &mut [u8]) -> io::Result { + Err(io::Error::other("test error")) + } + } + + let reader = io::BufReader::new(ReadMock); + let mut build_platforms = BuildPlatforms::new().expect("default ctor should succeed"); + build_platforms.set_host_libdir_from_rustc_output(reader); + assert_eq!(build_platforms.host_libdir, None); + + let reader = io::BufReader::new(ReadMock); + let mut build_platforms_target = + BuildPlatformsTarget::new(TargetTriple::x86_64_unknown_linux_gnu()); + build_platforms_target.set_libdir_from_rustc_output(reader); + assert_eq!(build_platforms_target.libdir, None); + } + + #[test] + fn test_read_from_rustc_output_empty_input() { + let mut build_platforms = BuildPlatforms::new().expect("default ctor should succeed"); + build_platforms.set_host_libdir_from_rustc_output(io::empty()); + assert_eq!(build_platforms.host_libdir, None); + + let mut build_platforms_target = + BuildPlatformsTarget::new(TargetTriple::x86_64_unknown_linux_gnu()); + build_platforms_target.set_libdir_from_rustc_output(io::empty()); + assert_eq!(build_platforms_target.libdir, None); + } + + #[test_case("/fake/libdir/22548", Some("/fake/libdir/22548"); "single line")] + #[test_case( + indoc! {r#" + /fake/libdir/1 + /fake/libdir/2 + "#}, + Some("/fake/libdir/1"); + "multiple lines" + )] + #[test_case( + "\t /fake/libdir\t \n\r", + Some("/fake/libdir"); + "with leading or trailing whitespace" + )] + #[test_case("\t \r\n", None; "empty content with whitespaces")] + fn test_read_from_rustc_output_not_empty_input(input: &str, actual: Option<&str>) { + let mut build_platforms = BuildPlatforms::new().expect("default ctor should succeed"); + build_platforms.set_host_libdir_from_rustc_output(Cursor::new(input)); + assert_eq!(build_platforms.host_libdir, actual.map(Utf8PathBuf::from)); + + let mut build_platforms_target = + BuildPlatformsTarget::new(TargetTriple::x86_64_unknown_linux_gnu()); + build_platforms_target.set_libdir_from_rustc_output(Cursor::new(input)); + assert_eq!(build_platforms_target.libdir, actual.map(Utf8PathBuf::from)); + } #[test] fn test_build_platform_new() { @@ -165,8 +296,22 @@ mod tests { build_platforms, BuildPlatforms { host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: None, target: None, } ); } + + #[test] + fn test_build_platforms_target_new() { + let triple = TargetTriple::x86_64_unknown_linux_gnu(); + let build_platforms_target = BuildPlatformsTarget::new(triple.clone()); + assert_eq!( + build_platforms_target, + BuildPlatformsTarget { + triple, + libdir: None, + } + ); + } } diff --git a/nextest-runner/tests/integration/fixtures.rs b/nextest-runner/tests/integration/fixtures.rs index 5b4097d4a90..72fcf4a09c8 100644 --- a/nextest-runner/tests/integration/fixtures.rs +++ b/nextest-runner/tests/integration/fixtures.rs @@ -175,6 +175,7 @@ pub(crate) static EXPECTED_TESTS: Lazy>> "with-build-script".into() => vec![ TestFixture { name: "tests::test_out_dir_present", status: FixtureStatus::Pass }, ], + "proc-macro-test".into() => vec![], } }, ); diff --git a/nextest-runner/tests/integration/target_runner.rs b/nextest-runner/tests/integration/target_runner.rs index 6e3245c9fbd..1e0716fb58d 100644 --- a/nextest-runner/tests/integration/target_runner.rs +++ b/nextest-runner/tests/integration/target_runner.rs @@ -13,8 +13,9 @@ use nextest_runner::{ signal::SignalHandlerKind, target_runner::{PlatformRunner, TargetRunner}, test_filter::{RunIgnored, TestFilterBuilder}, + RustcCli, }; -use std::env; +use std::{env, io::Cursor}; use target_spec::Platform; fn runner_for_target(triple: Option<&str>) -> Result<(BuildPlatforms, TargetRunner)> { @@ -26,8 +27,15 @@ fn runner_for_target(triple: Option<&str>) -> Result<(BuildPlatforms, TargetRunn ) .unwrap(); let mut build_platforms = BuildPlatforms::new()?; + if let Some(host_libdir) = RustcCli::print_host_libdir().read() { + build_platforms.set_host_libdir_from_rustc_output(Cursor::new(host_libdir)); + } if let Some(triple) = TargetTriple::find(&configs, triple)? { - build_platforms.target = Some(BuildPlatformsTarget { triple }); + let mut target = BuildPlatformsTarget::new(triple.clone()); + if let Some(libdir) = RustcCli::print_target_libdir(&triple).read() { + target.set_libdir_from_rustc_output(Cursor::new(libdir)); + } + build_platforms.target = Some(target); } let target_runner = TargetRunner::new(&configs, &build_platforms)?; Ok((build_platforms, target_runner)) From e9d36d991c29446a64bdcd9342b5061711656b1f Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 22 May 2024 16:09:53 -0700 Subject: [PATCH 5/5] Update nextest-metadata/src/test_list.rs --- nextest-metadata/src/test_list.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nextest-metadata/src/test_list.rs b/nextest-metadata/src/test_list.rs index 10230a02e55..2237efc9a4e 100644 --- a/nextest-metadata/src/test_list.rs +++ b/nextest-metadata/src/test_list.rs @@ -548,10 +548,12 @@ pub struct TargetPlatformSummary { #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct BuildPlatformsSummary { - /// The target platform used while compiling the Rust artifacts. + /// The host platform used while compiling the Rust artifacts. pub host: HostPlatformSummary, - /// The host platform used while compiling the Rust artifacts. + /// The target platforms used while compiling the Rust artifacts. + /// + /// With current versions of nextest, this will contain at most one element. pub targets: Vec, }