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 0c7d8e5e009..6feae8ef039 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,18 @@ impl BaseApp { Some(kind) => kind.binary_list.rust_build_meta.build_platforms.clone(), None => { let mut build_platform_builder = BuildPlatformBuilder::default(); + if let Some(output) = RustcCli::print_host_libdir().read() { + build_platform_builder.set_host_libdir(Cursor::new(output)); + } if let Some(target_triple) = discover_target_triple(&cargo_configs, cargo_opts.target.as_deref()) { - build_platform_builder.set_target(target_triple).add(); + let mut target_builder = + build_platform_builder.set_target(target_triple.clone()); + if let Some(output) = RustcCli::print_target_libdir(&target_triple).read() { + target_builder.set_libdir(Cursor::new(output)); + } + target_builder.add(); } build_platform_builder.build()? } 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..c853df3dd43 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,16 @@ 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_target = host_platform.triple_str(); + let output = CargoNextestCli::new() + .args(["list", "--target", host_target, "--message-format", "json"]) + .output(); + let result: TestListSummary = serde_json::from_slice(&output.stdout).unwrap(); + let build_platform = &result.rust_build_meta.target_platforms_v2[0]; + assert_eq!(build_platform.target.triple, host_target); + assert_eq!(build_platform.host_libdir, build_platform.target_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 ca073bd4193..9046e89b38d 100644 --- a/nextest-metadata/src/test_list.rs +++ b/nextest-metadata/src/test_list.rs @@ -521,6 +521,18 @@ pub struct RustNonTestBinarySummary { pub struct BuildPlatformSummary { /// The target platform, if specified. pub target: PlatformSummary, + + /// The libdir for the host platform. + /// + /// Empty if failed to discover. + #[serde(default)] + pub host_libdir: Option, + + /// The libdir for the target platform. + /// + /// Empty if failed to discover. + #[serde(default)] + pub target_libdir: Option, } /// Information about the kind of a Rust non-test binary. diff --git a/nextest-runner/src/config/test_helpers.rs b/nextest-runner/src/config/test_helpers.rs index 4e57c1926ff..634db73a9f4 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() -> NextestBuildPlatform { NextestBuildPlatform { 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(BuildPlatformTarget { 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 647f233fd67..235bbb4e5f4 100644 --- a/nextest-runner/src/list/binary_list.rs +++ b/nextest-runner/src/list/binary_list.rs @@ -399,6 +399,7 @@ mod tests { use indoc::indoc; use maplit::btreeset; use pretty_assertions::assert_eq; + use std::io::Cursor; use target_spec::{Platform, TargetFeatures}; #[test] @@ -427,8 +428,12 @@ mod tests { source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; + let fake_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"; let mut build_platform_builder = BuildPlatformBuilder::default(); - build_platform_builder.set_target(fake_triple).add(); + let mut build_platform_target_builder = build_platform_builder.set_target(fake_triple); + build_platform_target_builder.set_libdir(Cursor::new(fake_libdir)); + build_platform_target_builder.add(); + build_platform_builder.set_host_libdir(Cursor::new(fake_libdir)); let build_platforms = build_platform_builder.build().unwrap(); let mut rust_build_meta = RustBuildMeta::new("/fake/target", build_platforms); @@ -507,7 +512,9 @@ mod tests { "target": { "triple": "x86_64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "host-libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib", + "target-libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" } ], "target-platforms": [ diff --git a/nextest-runner/src/list/rust_build_meta.rs b/nextest-runner/src/list/rust_build_meta.rs index 590e0d2f628..d9326ceaa11 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::{RustBuildMetaSummary, RustNonTestBinarySummary}; use std::{ collections::{BTreeMap, BTreeSet}, @@ -83,8 +84,7 @@ impl RustBuildMeta { /// Empty metadata for tests. #[cfg(test)] pub(crate) fn empty() -> Self { - use target_spec::Platform; - + let build_platform_builder = crate::platform::BuildPlatformBuilder::default(); Self { target_directory: Utf8PathBuf::new(), base_output_directories: BTreeSet::new(), @@ -92,10 +92,7 @@ impl RustBuildMeta { build_script_out_dirs: BTreeMap::new(), linked_paths: BTreeMap::new(), state: PhantomData, - build_platforms: BuildPlatform { - host: Platform::current().unwrap(), - target: None, - }, + build_platforms: build_platform_builder.build().unwrap(), } } @@ -109,6 +106,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 +139,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() } } @@ -183,6 +199,7 @@ mod tests { platform::{BuildPlatformBuilder, BuildPlatformTarget}, }; use nextest_metadata::BuildPlatformSummary; + use std::io::Cursor; use target_spec::{summaries::PlatformSummary, Platform}; use test_case::test_case; @@ -219,6 +236,7 @@ mod tests { build_platforms: BuildPlatform { host: host_platform(), target: None, + host_libdir: None, }, ..Default::default() }; "no target platforms")] @@ -228,8 +246,10 @@ mod tests { }, RustBuildMeta:: { build_platforms: BuildPlatform { host: host_platform(), + host_libdir: None, target: Some(BuildPlatformTarget{ triple: TargetTriple::x86_64_unknown_linux_gnu(), + libdir: None, }), }, ..Default::default() @@ -241,8 +261,10 @@ mod tests { }, RustBuildMeta:: { build_platforms: BuildPlatform { host: host_platform(), + host_libdir: None, target: Some(BuildPlatformTarget{ - triple: x86_64_pc_windows_msvc_triple() + triple: x86_64_pc_windows_msvc_triple(), + libdir: None, }), }, ..Default::default() @@ -252,13 +274,17 @@ mod tests { target_platforms: vec![PlatformSummary::new("x86_64-pc-windows-msvc")], target_platforms_v2: vec![BuildPlatformSummary { target: PlatformSummary::new("x86_64-apple-darwin"), + host_libdir: Some("/fake/test/libdir/281".into()), + target_libdir: Some("/fake/test/libdir/837".into()), }], ..Default::default() }, RustBuildMeta:: { build_platforms: BuildPlatform { host: host_platform(), + host_libdir: Some("/fake/test/libdir/281".into()), target: Some(BuildPlatformTarget{ - triple: x86_64_apple_darwin_triple() + triple: x86_64_apple_darwin_triple(), + libdir: Some("/fake/test/libdir/837".into()), }), }, ..Default::default() @@ -283,21 +309,26 @@ mod tests { build_platforms: BuildPlatform { host: host_platform(), target: None, + host_libdir: None, }, ..Default::default() }, RustBuildMetaSummary { target_platform: None, target_platforms: vec![host_platform().to_summary()], target_platforms_v2: vec![BuildPlatformSummary{ - target: host_platform().to_summary() + target: host_platform().to_summary(), + host_libdir: None, + target_libdir: None, }], ..Default::default() }; "build platforms without target")] #[test_case(RustBuildMeta:: { build_platforms: BuildPlatform { host: host_platform(), + host_libdir: Some("/fake/test/libdir/736".into()), target: Some(BuildPlatformTarget { - triple: not_host_platform_triple() + triple: not_host_platform_triple(), + libdir: Some(Utf8PathBuf::from("/fake/test/libdir/873")), }), }, ..Default::default() @@ -305,7 +336,9 @@ mod tests { target_platform: Some(not_host_platform_triple().platform.triple_str().to_owned()), target_platforms: vec![not_host_platform_triple().platform.to_summary()], target_platforms_v2: vec![BuildPlatformSummary{ - target: not_host_platform_triple().platform.to_summary() + target: not_host_platform_triple().platform.to_summary(), + target_libdir: Some("/fake/test/libdir/873".into()), + host_libdir: Some("/fake/test/libdir/736".into()), }], ..Default::default() }; "build platforms with target")] @@ -313,4 +346,80 @@ 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 build_platform = { + let mut build_platform_builder = BuildPlatformBuilder::default(); + build_platform_builder.set_host_libdir(Cursor::new(host_libdir.as_str())); + let mut target_builder = + build_platform_builder.set_target(TargetTriple::x86_64_unknown_linux_gnu()); + target_builder.set_libdir(Cursor::new(target_libdir.as_str())); + target_builder.add(); + build_platform_builder + .build() + .expect("Should create BuildPlatforms with fake libdir successfully") + }; + + let rust_build_meta = RustBuildMeta { + build_platforms: build_platform, + ..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 builder = BuildPlatformBuilder::default(); + let mut target_builder = + builder.set_target(TargetTriple::x86_64_unknown_linux_gnu()); + target_builder.set_libdir(Cursor::new(target_libdir.as_str())); + target_builder.add(); + builder + .build() + .expect("Should create BuildPlatforms with fake libdir successfully") + }, + ..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 b55911c5834..b705133c81b 100644 --- a/nextest-runner/src/list/test_list.rs +++ b/nextest-runner/src/list/test_list.rs @@ -968,7 +968,7 @@ mod tests { use nextest_filtering::FilteringExpr; use nextest_metadata::{FilterMatch, MismatchReason}; use pretty_assertions::assert_eq; - use std::iter; + use std::{io::Cursor, iter}; use target_spec::Platform; #[test] @@ -1033,8 +1033,12 @@ mod tests { source: TargetTripleSource::CliOption, location: TargetDefinitionLocation::Builtin, }; + let fake_libdir = "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"; let mut build_platform_builder = BuildPlatformBuilder::default(); - build_platform_builder.set_target(fake_triple).add(); + let mut target_builder = build_platform_builder.set_target(fake_triple); + target_builder.set_libdir(Cursor::new(fake_libdir)); + target_builder.add(); + build_platform_builder.set_host_libdir(Cursor::new(fake_libdir)); let build_platforms = build_platform_builder.build().unwrap(); let fake_env = EnvironmentMap::empty(); let rust_build_meta = @@ -1146,7 +1150,9 @@ mod tests { "target": { "triple": "aarch64-unknown-linux-gnu", "target-features": "unknown" - } + }, + "host-libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib", + "target-libdir": "/home/fake/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" } ], "target-platforms": [ diff --git a/nextest-runner/src/platform.rs b/nextest-runner/src/platform.rs index d8a66f64925..be40751071a 100644 --- a/nextest-runner/src/platform.rs +++ b/nextest-runner/src/platform.rs @@ -7,13 +7,37 @@ use crate::{ cargo_config::{CargoTargetArg, TargetTriple}, errors::{RustBuildMetaParseError, TargetTripleError, UnknownHostPlatform}, }; +use camino::Utf8PathBuf; use nextest_metadata::BuildPlatformSummary; +use std::io::{self, Cursor}; use target_spec::summaries::PlatformSummary; pub use target_spec::Platform; +fn read_first_line_as_path(mut 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. + let mut line = Default::default(); + match reader.read_line(&mut line) { + Ok(_) => { + 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)) + } + Err(e) => { + log::debug!("failed to read the input: {:#?}", e); + None + } + } +} + /// Creates a new [`BuildPlatform`]. #[derive(Default)] pub struct BuildPlatformBuilder { + host_libdir: Option, target: Option, } @@ -24,16 +48,29 @@ impl BuildPlatformBuilder { /// Returns an error if the host platform could not be determined. pub fn set_target(&mut self, triple: TargetTriple) -> BuildPlatformTargetBuilder<'_> { BuildPlatformTargetBuilder { + libdir: None, parent: self, triple, } } + /// Set the host libdir to the built [`BuildPlatform`]. + /// + /// Used to set the dynamic linker search path when running the test executables. If we fail to + /// parse the input, the libdir won't be set. + pub fn set_host_libdir(&mut self, reader: impl io::BufRead) { + self.host_libdir = read_first_line_as_path(reader); + } + /// Creates a new [`BuildPlatform`]. + /// + /// # Error + /// Returns an error if the host platform could not be determined. pub fn build(self) -> Result { let host = Platform::current().map_err(|error| UnknownHostPlatform { error })?; Ok(BuildPlatform { host, + host_libdir: self.host_libdir, target: self.target, }) } @@ -44,13 +81,23 @@ impl BuildPlatformBuilder { pub struct BuildPlatformTargetBuilder<'a> { parent: &'a mut BuildPlatformBuilder, triple: TargetTriple, + libdir: Option, } impl<'a> BuildPlatformTargetBuilder<'a> { + /// Add the libdir to the target. + /// + /// Used to set the dynamic linker search path when running the test executables. If we fail to + /// parse the input, the libdir won't be set. + pub fn set_libdir(&mut self, reader: impl io::BufRead) { + self.libdir = read_first_line_as_path(reader); + } + /// Build a [`BuildPlatform`] and add it to the [`BuildPlatformBuilder`]. pub fn add(self) { self.parent.target = Some(BuildPlatformTarget { triple: self.triple, + libdir: self.libdir, }); } } @@ -60,6 +107,9 @@ impl<'a> BuildPlatformTargetBuilder<'a> { pub struct BuildPlatformTarget { /// The target triplet, which consists of machine, vendor and OS. pub triple: TargetTriple, + + /// The target libdir. + pub libdir: Option, } /// A representation of host and target platform. @@ -68,6 +118,9 @@ pub struct BuildPlatform { /// The host platform. pub host: Platform, + /// The host libdir. + pub host_libdir: Option, + /// The target platform, if specified. pub target: Option, } @@ -128,14 +181,18 @@ impl From for PlatformSummary { /// triple is detected if `target_triple` is `None`. impl From for BuildPlatformSummary { fn from(value: BuildPlatform) -> Self { - let target = { + let (target, target_libdir) = { if let Some(target) = &value.target { - target.triple.platform.to_summary() + (target.triple.platform.to_summary(), &target.libdir) } else { - value.host.to_summary() + (value.host.to_summary(), &value.host_libdir) } }; - Self { target } + Self { + target, + target_libdir: target_libdir.clone(), + host_libdir: value.host_libdir, + } } } @@ -162,9 +219,15 @@ impl TryFrom for BuildPlatform { fn try_from(summary: BuildPlatformSummary) -> Result { let mut builder = BuildPlatformBuilder::default(); if let Some(target_triple) = TargetTriple::deserialize(Some(summary.target))? { - let target_builder = builder.set_target(target_triple); + let mut target_builder = builder.set_target(target_triple); + if let Some(libdir) = summary.target_libdir { + target_builder.set_libdir(Cursor::new(libdir.into_string())); + } target_builder.add(); } + if let Some(host_libdr) = summary.host_libdir { + builder.set_host_libdir(Cursor::new(host_libdr.to_string())); + } builder .build() .map_err(|error| RustBuildMetaParseError::UnknownHostPlatform(error.error)) @@ -174,6 +237,54 @@ impl TryFrom for BuildPlatform { #[cfg(test)] mod tests { use super::*; + use indoc::indoc; + + #[test] + fn test_read_first_line_input_read_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); + assert_eq!(read_first_line_as_path(reader), None); + } + + #[test] + fn test_read_first_line_empty_input() { + assert_eq!(read_first_line_as_path(Cursor::new("")), None); + } + + #[test] + fn test_read_first_line_non_empty_inputs() { + // single line + let fake_libdir = "/fake/libdir/22548"; + assert_eq!( + read_first_line_as_path(Cursor::new(fake_libdir)), + Some(Utf8PathBuf::from(fake_libdir)) + ); + + // multiple lines + let fake_libdir: &str = indoc! {r#" + /fake/libdir/1 + /fake/libdir/2 + "#}; + assert_eq!( + read_first_line_as_path(Cursor::new(fake_libdir)), + Some(Utf8PathBuf::from("/fake/libdir/1")), + ); + + // with leading/trailing whitespace + let fake_libdir = "\t /fake/libdir\t \n\r"; + assert_eq!( + read_first_line_as_path(Cursor::new(fake_libdir)), + Some(Utf8PathBuf::from("/fake/libdir")), + ); + + // empty content with whitespaces + assert_eq!(read_first_line_as_path(Cursor::new("\t \r\n")), None); + } #[test] fn test_build_platform_builder_default() { @@ -184,6 +295,24 @@ mod tests { build_platform, BuildPlatform { host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: None, + target: None, + } + ); + } + + #[test] + fn test_build_platform_builder_with_host_libdir() { + let mut build_platform_builder = BuildPlatformBuilder::default(); + build_platform_builder.set_host_libdir(Cursor::new("/fake/host/libdir")); + let build_platform = build_platform_builder + .build() + .expect("default builder should build successfully"); + assert_eq!( + build_platform, + BuildPlatform { + host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: Some("/fake/host/libdir".into()), target: None, } ); @@ -202,8 +331,29 @@ mod tests { build_platform, BuildPlatform { host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: None, + target: Some(BuildPlatformTarget { + triple: TargetTriple::x86_64_unknown_linux_gnu(), + libdir: None, + }) + } + ); + + let mut builder = BuildPlatformBuilder::default(); + let mut target_builder = builder.set_target(TargetTriple::x86_64_unknown_linux_gnu()); + target_builder.set_libdir(Cursor::new("/fake/target/libdir")); + target_builder.add(); + let build_platform = builder + .build() + .expect("should build with target successfully"); + assert_eq!( + build_platform, + BuildPlatform { + host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: None, target: Some(BuildPlatformTarget { triple: TargetTriple::x86_64_unknown_linux_gnu(), + libdir: Some("/fake/target/libdir".into()), }) } ); @@ -220,6 +370,7 @@ mod tests { build_platform, BuildPlatform { host: Platform::current().expect("should detect the current platform successfully"), + host_libdir: None, target: None, } ); diff --git a/nextest-runner/tests/integration/fixtures.rs b/nextest-runner/tests/integration/fixtures.rs index b7fef7585b0..bafd218bbad 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 a634d24b2ae..b7c700fae0d 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<(BuildPlatform, TargetRunner)> { @@ -26,8 +27,15 @@ fn runner_for_target(triple: Option<&str>) -> Result<(BuildPlatform, TargetRunne ) .unwrap(); let mut build_platform_builder = BuildPlatformBuilder::default(); + if let Some(host_libdir) = RustcCli::print_host_libdir().read() { + build_platform_builder.set_host_libdir(Cursor::new(host_libdir)); + } if let Some(triple) = TargetTriple::find(&configs, triple)? { - build_platform_builder.set_target(triple).add(); + let mut target_builder = build_platform_builder.set_target(triple.clone()); + if let Some(libdir) = RustcCli::print_target_libdir(&triple).read() { + target_builder.set_libdir(Cursor::new(libdir)); + } + target_builder.add(); } let build_platforms = build_platform_builder.build()?; let target_runner = TargetRunner::new(&configs, &build_platforms)?;