diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index a619aa6e5427b..897b8af0eeed1 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -619,7 +619,7 @@ impl Vec { /// use std::alloc::{alloc, Layout}; /// /// fn main() { - /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// let layout = Layout::array::(16).expect("16 u32s take 64 bytes, so it shouldn't overflow"); /// /// let vec = unsafe { /// let mem = alloc(layout).cast::(); @@ -719,7 +719,7 @@ impl Vec { /// use std::ptr::NonNull; /// /// fn main() { - /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// let layout = Layout::array::(16).expect("16 u32s take 64 bytes, so it shouldn't overflow"); /// /// let vec = unsafe { /// let Some(mem) = NonNull::new(alloc(layout).cast::()) else { @@ -1162,7 +1162,7 @@ impl Vec { /// use std::alloc::{AllocError, Allocator, Global, Layout}; /// /// fn main() { - /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// let layout = Layout::array::(16).expect("16 u32s take 64 bytes, so it shouldn't overflow"); /// /// let vec = unsafe { /// let mem = match Global.allocate(layout) { @@ -1277,7 +1277,7 @@ impl Vec { /// use std::alloc::{AllocError, Allocator, Global, Layout}; /// /// fn main() { - /// let layout = Layout::array::(16).expect("overflow cannot happen"); + /// let layout = Layout::array::(16).expect("16 u32s take 64 bytes, so it shouldn't overflow"); /// /// let vec = unsafe { /// let mem = match Global.allocate(layout) { @@ -1521,7 +1521,7 @@ impl Vec { /// /// Ok(output) /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # process_data(&[1, 2, 3]).expect("this test needs 12 bytes, so it shouldn't fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1564,7 +1564,7 @@ impl Vec { /// /// Ok(output) /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// # process_data(&[1, 2, 3]).expect("this test needs 12 bytes, so it shouldn't fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1648,7 +1648,7 @@ impl Vec { /// let mut vec = Vec::with_capacity(10); /// vec.extend([1, 2, 3]); /// assert!(vec.capacity() >= 10); - /// vec.try_shrink_to_fit().expect("why is the test harness failing to shrink to 12 bytes"); + /// vec.try_shrink_to_fit().expect("for this test, shrink shouldn't fail"); /// assert!(vec.capacity() >= 3); /// ``` #[unstable(feature = "vec_fallible_shrink", issue = "152350")] @@ -1678,7 +1678,7 @@ impl Vec { /// let mut vec = Vec::with_capacity(10); /// vec.extend([1, 2, 3]); /// assert!(vec.capacity() >= 10); - /// vec.try_shrink_to(4).expect("why is the test harness failing to shrink to 12 bytes"); + /// vec.try_shrink_to(4).expect("for this test, shrink shouldn't fail"); /// assert!(vec.capacity() >= 4); /// vec.try_shrink_to(0).expect("this is a no-op and thus the allocator isn't involved."); /// assert!(vec.capacity() >= 3); @@ -3688,7 +3688,10 @@ impl Vec<[T; N], A> { pub fn into_flattened(self) -> Vec { let (ptr, len, cap, alloc) = self.into_raw_parts_with_alloc(); let (new_len, new_cap) = if T::IS_ZST { - (len.checked_mul(N).expect("vec len overflow"), usize::MAX) + ( + len.checked_mul(N).expect("the product of vec len and N shouldn't overflow"), + usize::MAX, + ) } else { // SAFETY: // - `cap * N` cannot overflow because the allocation is already in diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 4620a9f373d6c..077005afd5d38 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2558,7 +2558,7 @@ fn test_extend_from_within_panicking_clone() { } #[test] -#[should_panic = "vec len overflow"] +#[should_panic = "the product of vec len and N shouldn't overflow"] fn test_into_flattened_size_overflow() { let v = vec![[(); usize::MAX]; 2]; let _ = v.into_flattened(); diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 63a99349d7c9a..30af6450d19a9 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -410,23 +410,41 @@ where const impl PartialOrd for [T; N] { #[inline] fn partial_cmp(&self, other: &[T; N]) -> Option { - PartialOrd::partial_cmp(&&self[..], &&other[..]) + <[T] as PartialOrd>::partial_cmp(self, other) } + #[inline] fn lt(&self, other: &[T; N]) -> bool { - PartialOrd::lt(&&self[..], &&other[..]) + <[T] as PartialOrd>::lt(self, other) } #[inline] fn le(&self, other: &[T; N]) -> bool { - PartialOrd::le(&&self[..], &&other[..]) + <[T] as PartialOrd>::le(self, other) } #[inline] fn ge(&self, other: &[T; N]) -> bool { - PartialOrd::ge(&&self[..], &&other[..]) + <[T] as PartialOrd>::ge(self, other) } #[inline] fn gt(&self, other: &[T; N]) -> bool { - PartialOrd::gt(&&self[..], &&other[..]) + <[T] as PartialOrd>::gt(self, other) + } + + #[inline] + fn __chaining_lt(&self, other: &[T; N]) -> ControlFlow { + <[T] as PartialOrd>::__chaining_lt(self, other) + } + #[inline] + fn __chaining_le(&self, other: &[T; N]) -> ControlFlow { + <[T] as PartialOrd>::__chaining_le(self, other) + } + #[inline] + fn __chaining_ge(&self, other: &[T; N]) -> ControlFlow { + <[T] as PartialOrd>::__chaining_ge(self, other) + } + #[inline] + fn __chaining_gt(&self, other: &[T; N]) -> ControlFlow { + <[T] as PartialOrd>::__chaining_gt(self, other) } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 6269c9fcf9136..43128302dfef4 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -743,6 +743,54 @@ impl Clone for Reverse { } } +/// A pair where ordering and equality work on only the `key`, ignoring the `value`. +/// +/// Used to implement `Iterator::min_by_key` as `map`+`min`, for example. +#[derive(Debug, Copy, Clone)] +pub(crate) struct KeyAndValue { + pub key: K, + pub value: V, +} +impl PartialEq for KeyAndValue { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.key == other.key + } + #[inline] + fn ne(&self, other: &Self) -> bool { + self.key != other.key + } +} +impl Eq for KeyAndValue {} +impl PartialOrd for KeyAndValue { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(&self.key, &other.key) + } + #[inline] + fn lt(&self, other: &Self) -> bool { + self.key < other.key + } + #[inline] + fn le(&self, other: &Self) -> bool { + self.key <= other.key + } + #[inline] + fn gt(&self, other: &Self) -> bool { + self.key > other.key + } + #[inline] + fn ge(&self, other: &Self) -> bool { + self.key >= other.key + } +} +impl Ord for KeyAndValue { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.key, &other.key) + } +} + /// Trait for types that form a [total order](https://en.wikipedia.org/wiki/Total_order). /// /// Implementations must be consistent with the [`PartialOrd`] implementation, and ensure `max`, diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 3867a44099f6d..28b5bba6be207 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -6,7 +6,7 @@ use super::super::{ }; use super::TrustedLen; use crate::array; -use crate::cmp::{self, Ordering}; +use crate::cmp::{self, KeyAndValue, Ordering}; use crate::marker::Destruct; use crate::num::NonZero; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; @@ -3248,7 +3248,7 @@ pub const trait Iterator { Self: Sized, Self::Item: Ord, { - self.max_by(Ord::cmp) + self.reduce(Ord::max) } /// Returns the minimum element of an iterator. @@ -3285,7 +3285,7 @@ pub const trait Iterator { Self: Sized, Self::Item: Ord, { - self.min_by(Ord::cmp) + self.reduce(Ord::min) } /// Returns the element that gives the maximum value from the @@ -3308,18 +3308,17 @@ pub const trait Iterator { Self: Sized, F: FnMut(&Self::Item) -> B, { - #[inline] - fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { - move |x| (f(&x), x) - } + // If we implemented this via `max_by` that would force it to use `B::cmp`. + // By using `max` over `KeyAndValue`, it instead ends up calling `B::lt` + // (via `KeyAndValue::max`), which is often overridden more efficiently. #[inline] - fn compare((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering { - x_p.cmp(y_p) + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> KeyAndValue { + move |value| KeyAndValue { key: f(&value), value } } - let (_, x) = self.map(key(f)).max_by(compare)?; - Some(x) + let KeyAndValue { value, .. } = self.map(key(f)).max()?; + Some(value) } /// Returns the element that gives the maximum value with respect to the @@ -3370,18 +3369,17 @@ pub const trait Iterator { Self: Sized, F: FnMut(&Self::Item) -> B, { - #[inline] - fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> (B, T) { - move |x| (f(&x), x) - } + // If we implemented this via `min_by` that would force it to use `B::cmp`. + // By using `min` over `KeyAndValue`, it instead ends up calling `B::lt` + // (via `KeyAndValue::min`), which is often overridden more efficiently. #[inline] - fn compare((x_p, _): &(B, T), (y_p, _): &(B, T)) -> Ordering { - x_p.cmp(y_p) + fn key(mut f: impl FnMut(&T) -> B) -> impl FnMut(T) -> KeyAndValue { + move |value| KeyAndValue { key: f(&value), value } } - let (_, x) = self.map(key(f)).min_by(compare)?; - Some(x) + let KeyAndValue { value, .. } = self.map(key(f)).min()?; + Some(value) } /// Returns the element that gives the minimum value with respect to the diff --git a/library/coretests/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs index 0850b1e4edc26..e4c3459e0bff9 100644 --- a/library/coretests/tests/iter/traits/iterator.rs +++ b/library/coretests/tests/iter/traits/iterator.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::cmp::Ordering; use core::iter::zip; use core::num::NonZero; @@ -17,13 +18,13 @@ impl PartialEq for Mod3 { impl Eq for Mod3 {} impl PartialOrd for Mod3 { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Mod3 { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { + fn cmp(&self, other: &Self) -> Ordering { (self.0 % 3).cmp(&(other.0 % 3)) } } @@ -75,8 +76,6 @@ fn test_lt() { #[test] fn test_cmp_by() { - use core::cmp::Ordering; - let f = |x: i32, y: i32| (x * x).cmp(&y); let xs = || [1, 2, 3, 4].iter().copied(); let ys = || [1, 4, 16].iter().copied(); @@ -91,8 +90,6 @@ fn test_cmp_by() { #[test] fn test_partial_cmp_by() { - use core::cmp::Ordering; - let f = |x: i32, y: i32| (x * x).partial_cmp(&y); let xs = || [1, 2, 3, 4].iter().copied(); let ys = || [1, 4, 16].iter().copied(); @@ -722,3 +719,62 @@ fn _empty_impl_all_auto_traits() { all_auto_traits::>(); } + +#[test] +fn test_iterator_min_max_use_ord_min_max() { + // There's no stable guarantee that the iterator methods use these, but they were added + // on `Ord` (as opposed to just the functions in `cmp`) so that they could be overridden + // when a more efficient implementation is available, so we should probably use them. + + let a = [OnlyMinMax(3), OnlyMinMax(1), OnlyMinMax(5), OnlyMinMax(9), OnlyMinMax(7)]; + assert_eq!(a.iter().copied().min(), Some(OnlyMinMax(1))); + assert_eq!(a.iter().copied().max(), Some(OnlyMinMax(9))); + + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + struct OnlyMinMax(i32); + impl PartialOrd for OnlyMinMax { + fn partial_cmp(&self, _other: &Self) -> Option { + unimplemented!() + } + } + impl Ord for OnlyMinMax { + fn cmp(&self, _other: &Self) -> Ordering { + unimplemented!() + } + fn min(self, other: Self) -> Self { + Self(Ord::min(self.0, other.0)) + } + fn max(self, other: Self) -> Self { + Self(Ord::max(self.0, other.0)) + } + } +} + +#[test] +fn test_iterator_min_max_by_key_use_lt() { + // The exact method is certainly not a stable guarantee. The important part + // is that they use a simple `-> bool` method as opposed to three-way `cmp`. + // If they used `gt` instead, or something, that wouldn't be the end of the world, + // but `lt` tends to be best optimized because that's the one that C++ has + // traditionally used in standard library templates. + + let a = [3, 1, 5, 9, 7]; + assert_eq!(a.iter().copied().min_by_key(|x| OnlyLt(*x)), Some(1)); + assert_eq!(a.iter().copied().max_by_key(|x| OnlyLt(*x)), Some(9)); + + #[derive(Debug, Copy, Clone, Eq, PartialEq)] + struct OnlyLt(i32); + impl PartialOrd for OnlyLt { + fn partial_cmp(&self, _other: &Self) -> Option { + unimplemented!() + } + fn lt(&self, other: &Self) -> bool { + self.0 < other.0 + } + } + impl Ord for OnlyLt { + fn cmp(&self, _other: &Self) -> Ordering { + unimplemented!() + } + } +} diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 2a656e2f8c196..0cc53b5ab546f 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1510,6 +1510,7 @@ fn open_flavors() { // This error string is set by std itself so we are not at the whim of the OS here. let invalid_options = "creating or truncating a file requires write or append access"; + let append_truncate_error = "append and truncate cannot both be enabled"; // Test various combinations of creation modes and access modes. // @@ -1547,15 +1548,21 @@ fn open_flavors() { // append check!(c(&a).create_new(true).open(&tmpdir.join("d"))); - error_contains!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options); - error_contains!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options); + error_contains!( + c(&a).create(true).truncate(true).open(&tmpdir.join("d")), + append_truncate_error + ); + error_contains!(c(&a).truncate(true).open(&tmpdir.join("d")), append_truncate_error); check!(c(&a).create(true).open(&tmpdir.join("d"))); check!(c(&a).open(&tmpdir.join("d"))); // read-append check!(c(&ra).create_new(true).open(&tmpdir.join("e"))); - error_contains!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options); - error_contains!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options); + error_contains!( + c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), + append_truncate_error + ); + error_contains!(c(&ra).truncate(true).open(&tmpdir.join("e")), append_truncate_error); check!(c(&ra).create(true).open(&tmpdir.join("e"))); check!(c(&ra).open(&tmpdir.join("e"))); @@ -2368,7 +2375,6 @@ fn test_open_options_invalid_combinations() { (|| OO::new().create(true).read(true).clone(), "create without write"), (|| OO::new().create_new(true).read(true).clone(), "create_new without write"), (|| OO::new().truncate(true).read(true).clone(), "truncate without write"), - (|| OO::new().truncate(true).append(true).clone(), "truncate with append"), ]; for (make_opts, desc) in test_cases { @@ -2383,7 +2389,13 @@ fn test_open_options_invalid_combinations() { "{desc} - wrong error message" ); } + let result = OO::new().truncate(true).append(true).open("nonexistent.txt"); + assert!(result.is_err(), "truncate with append should fail"); + + let err = result.unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); + assert_eq!(err.to_string(), "append and truncate cannot both be enabled"); let result = OO::new().open("nonexistent.txt"); assert!(result.is_err(), "no access mode should fail"); let err = result.unwrap_err(); diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index cdd1ef6146fd9..885e56ed2c6b1 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1191,7 +1191,7 @@ impl OpenOptions { if self.truncate && !self.create_new { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "creating or truncating a file requires write or append access", + "append and truncate cannot both be enabled", )); } } diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index ef76a038c1fb5..c1d21d07d9952 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -301,7 +301,7 @@ impl OpenOptions { if self.truncate && !self.create_new { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "creating or truncating a file requires write or append access", + "append and truncate cannot both be enabled", )); } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 8fa1ce956c3ca..6776487b5870e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2291,9 +2291,6 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the target, }); } - if mode == CompiletestMode::RunMake { - builder.tool_exe(Tool::RunMakeSupport); - } // ensure that `libproc_macro` is available on the host. if suite == "mir-opt" { @@ -2306,6 +2303,36 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let mut cmd = builder.tool_cmd(Tool::Compiletest); + if mode == CompiletestMode::RunMake { + // Find .rlib and .rmeta files of the run-make-support library, and pass them to + // compiletest + let output = builder.tool(Tool::RunMakeSupport); + let find = |extension: &str| -> Option<&PathBuf> { + output.artifacts.iter().find_map(|p| { + // We want librun_make_support .rlib and .rmeta files + // They can be in separate directories, because Cargo currently uplifts the + // .rlib file when using -Zembed-metadata=no, but it doesn't uplift the + // .rmeta file + let filename = p.file_name()?.to_str()?; + if !filename.starts_with("librun_make_support") { + return None; + } + + if extension == p.extension()? { Some(p) } else { None } + }) + }; + if !builder.config.dry_run() { + let rlib = + find("rlib").expect(".rlib not found when compiling librun_make_support"); + cmd.arg("--run-make-support-rlib").arg(rlib); + + // .rmeta might not be found if we're not using -Zembed-metadata=no + if let Some(rmeta) = find("rmeta") { + cmd.arg("--run-make-support-rmeta").arg(rmeta); + } + } + } + if suite == "mir-opt" { builder.ensure(compile::Std::new(test_compiler, target).is_for_mir_opt_tests(true)); } else { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 3605fcf5b2fa6..75d5fdcdd2c33 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -13,7 +13,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::{env, fs}; -use crate::core::build_steps::compile::is_lto_stage; +use crate::core::build_steps::compile::{CargoMessage, is_lto_stage}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{ @@ -63,6 +63,8 @@ pub struct ToolBuildResult { pub tool_path: PathBuf, /// Compiler used to build the tool. pub build_compiler: Compiler, + /// All Cargo artifacts produced during the compilation of this tool + pub artifacts: Vec, } impl Step for ToolBuild { @@ -152,7 +154,14 @@ impl Step for ToolBuild { builder.msg(Kind::Build, self.tool, self.mode, self.build_compiler, self.target); // we check this below - let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |_| {}); + let mut artifacts = vec![]; + let build_success = compile::stream_cargo(builder, cargo, vec![], &mut |msg| match msg { + CargoMessage::CompilerArtifact { filenames, .. } => { + artifacts.extend(filenames.into_iter().map(|p| PathBuf::from(p.as_ref()))); + } + CargoMessage::BuildScriptExecuted => {} + CargoMessage::BuildFinished => {} + }); builder.save_toolstate( tool, @@ -177,7 +186,7 @@ impl Step for ToolBuild { .join(format!("lib{tool}.rlib")), }; - ToolBuildResult { tool_path, build_compiler: self.build_compiler } + ToolBuildResult { tool_path, build_compiler: self.build_compiler, artifacts } } } } @@ -409,12 +418,19 @@ macro_rules! bootstrap_tool { /// /// The actual building, if any, will be handled via [`ToolBuild`]. pub fn tool_exe(&self, tool: Tool) -> PathBuf { + self.tool(tool).tool_path + } + + /// Ensure a tool is built, then return its build output. + /// + /// The actual building, if any, will be handled via [`ToolBuild`]. + pub fn tool(&self, tool: Tool) -> ToolBuildResult { match tool { $(Tool::$name => self.ensure($name { compiler: self.compiler(0, self.config.host_target), target: self.config.host_target, - }).tool_path, + }), )+ } } @@ -1524,7 +1540,7 @@ fn build_extended_rustc_tool( ) -> ToolBuildResult { let target = compilers.target(); let build_compiler = compilers.build_compiler; - let ToolBuildResult { tool_path, .. } = builder.ensure(ToolBuild { + let ToolBuildResult { tool_path, artifacts, .. } = builder.ensure(ToolBuild { build_compiler, target, tool: tool_name, @@ -1551,9 +1567,9 @@ fn build_extended_rustc_tool( // Return a path into the bin dir. let path = bindir.join(exe(tool_name, target_compiler.host)); - ToolBuildResult { tool_path: path, build_compiler } + ToolBuildResult { tool_path: path, build_compiler, artifacts } } else { - ToolBuildResult { tool_path, build_compiler } + ToolBuildResult { tool_path, build_compiler, artifacts } } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 499900226d026..d5e2ef1579dc1 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2388,12 +2388,12 @@ mod snapshot { insta::assert_snapshot!( ctx.config("test") .path("run-make") - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 0 -> RunMakeSupport 1 [build] rustc 1 -> std 1 [build] rustc 0 -> Compiletest 1 + [build] rustc 0 -> RunMakeSupport 1 [build] rustdoc 1 [test] compiletest-run-make 1 "); @@ -2405,12 +2405,12 @@ mod snapshot { insta::assert_snapshot!( ctx.config("test") .path("run-make-cargo") - .render_steps(), @r" + .render_steps(), @" [build] llvm [build] rustc 0 -> rustc 1 - [build] rustc 0 -> RunMakeSupport 1 [build] rustc 1 -> std 1 [build] rustc 0 -> Compiletest 1 + [build] rustc 0 -> RunMakeSupport 1 [build] rustc 0 -> cargo 1 [build] rustdoc 1 [test] compiletest-run-make-cargo 1 diff --git a/src/tools/compiletest/src/cli.rs b/src/tools/compiletest/src/cli.rs index 3af7b4dfeaeac..7efeec70b2af7 100644 --- a/src/tools/compiletest/src/cli.rs +++ b/src/tools/compiletest/src/cli.rs @@ -117,6 +117,12 @@ struct Args { /// Path to rustc to use for compiling run-make recipes. #[arg(long)] stage0_rustc_path: Option, + /// Path to librun-make-support .rlib to use for compiling run-make recipes. + #[arg(long)] + run_make_support_rlib: Option, + /// Path to librun-make-support .rmeta to use for compiling run-make recipes. + #[arg(long)] + run_make_support_rmeta: Option, /// Path to rustc to use for querying target information. #[arg(long)] query_rustc_path: Option, @@ -396,122 +402,128 @@ pub(crate) fn parse_config(args: Vec) -> Config { CodegenBackend::Llvm | CodegenBackend::Cranelift => vec![], }; + // FIXME: this run scheme is... confusing. + let run = args.run.and_then(|mode| match mode.as_str() { + "auto" => None, + "always" => Some(true), + "never" => Some(false), + _ => panic!("unknown `--run` option `{}` given", mode), + }); + Config { + // tidy-alphabetical-start + adb_device_status, + adb_path: args.adb_path, + adb_test_dir: args.adb_test_dir, + android_cross_path: args.android_cross_path, + ar: args.ar, bless: args.bless, - fail_fast: args.fail_fast || env::var_os("RUSTC_TEST_FAIL_FAST").is_some(), + build_root, + build_test_suite_root, - host_compile_lib_path: make_absolute(args.compile_lib_path), - target_run_lib_path: make_absolute(args.run_lib_path), - rustc_path: args.rustc_path, - cargo_path: args.cargo_path, - stage0_rustc_path: args.stage0_rustc_path, - query_rustc_path: args.query_rustc_path, - rustdoc_path: args.rustdoc_path, - coverage_dump_path: args.coverage_dump_path, - python: args.python, - jsondocck_path: args.jsondocck_path, - jsondoclint_path: args.jsondoclint_path, - run_clang_based_tests_with: args.run_clang_based_tests_with, - llvm_filecheck: args.llvm_filecheck, - llvm_bin_dir: args.llvm_bin_dir, + builtin_cfg_names: OnceLock::new(), + bypass_ignore_backends: args.bypass_ignore_backends, - src_root, - src_test_suite_root, + capture: !args.no_capture, - build_root, - build_test_suite_root, + cargo_path: args.cargo_path, + cc: args.cc, + cdb: args.cdb, + cdb_version, + cflags: args.cflags, + channel: args.channel, + compare_mode, + coverage_dump_path: args.coverage_dump_path, + cxx: args.cxx, + cxxflags: args.cxxflags, + default_codegen_backend, + diff_command: args.compiletest_diff_tool, - sysroot_base: args.sysroot_base, + edition: args.edition, - stage: args.stage, - stage_id: args.stage_id, + fail_fast: args.fail_fast || env::var_os("RUSTC_TEST_FAIL_FAST").is_some(), - mode, - suite: args.suite, - run_ignored: args.ignored, - with_rustc_debug_assertions: args.with_rustc_debug_assertions, - with_std_debug_assertions: args.with_std_debug_assertions, - with_std_remap_debuginfo: args.with_std_remap_debuginfo, - filters, - skip: args.skip, filter_exact: args.exact, + filters, force_pass_mode: args.pass, - // FIXME: this run scheme is... confusing. - run: args.run.and_then(|mode| match mode.as_str() { - "auto" => None, - "always" => Some(true), - "never" => Some(false), - _ => panic!("unknown `--run` option `{}` given", mode), - }), - runner: args.runner, - host_rustcflags: args.host_rustcflags, - target_rustcflags: args.target_rustcflags, - optimize_tests: args.optimize_tests, - rust_randomized_layout: args.rust_randomized_layout, - target: args.target, - host: args.host, - cdb: args.cdb, - cdb_version, + force_rerun: args.force_rerun, + + gcc_supported_target_tuples, + gdb: args.gdb, gdb_version, - lldb: args.lldb, - lldb_version, - llvm_version, - system_llvm: args.system_llvm, - android_cross_path: args.android_cross_path, - adb_path: args.adb_path, - adb_test_dir: args.adb_test_dir, - adb_device_status, - verbose: args.verbose, - verbose_run_make_subprocess_output: args.verbose_run_make_subprocess_output, - only_modified: args.only_modified, - remote_test_client: args.remote_test_client, - compare_mode, - rustfix_coverage: args.rustfix_coverage, - has_enzyme: args.has_enzyme, - has_offload: args.has_offload, - channel: args.channel, git_hash: args.git_hash, - edition: args.edition, + git_merge_commit_email: args.git_merge_commit_email, - cc: args.cc, - cxx: args.cxx, - cflags: args.cflags, - cxxflags: args.cxxflags, - ar: args.ar, - target_linker: args.target_linker, + has_enzyme: args.has_enzyme, + has_offload: args.has_offload, + host: args.host, + host_compile_lib_path: make_absolute(args.compile_lib_path), host_linker: args.host_linker, - llvm_components: args.llvm_components, - nodejs: args.nodejs, - - force_rerun: args.force_rerun, + host_rustcflags: args.host_rustcflags, + iteration_count, + jobs: args.jobs, - target_cfgs: OnceLock::new(), - builtin_cfg_names: OnceLock::new(), - supported_crate_types: OnceLock::new(), + jsondocck_path: args.jsondocck_path, + jsondoclint_path: args.jsondoclint_path, + lldb: args.lldb, + lldb_version, + llvm_bin_dir: args.llvm_bin_dir, - capture: !args.no_capture, + llvm_components: args.llvm_components, + llvm_filecheck: args.llvm_filecheck, + llvm_version, + minicore_path: args.minicore_path, + mode, nightly_branch: args.nightly_branch, - git_merge_commit_email: args.git_merge_commit_email, + nodejs: args.nodejs, + only_modified: args.only_modified, + optimize_tests: args.optimize_tests, + override_codegen_backend: args.override_codegen_backend, + parallel_frontend_threads, profiler_runtime: args.profiler_runtime, - diff_command: args.compiletest_diff_tool, + python: args.python, + query_rustc_path: args.query_rustc_path, + remote_test_client: args.remote_test_client, + run, + run_clang_based_tests_with: args.run_clang_based_tests_with, + run_ignored: args.ignored, + run_make_support_rlib: args.run_make_support_rlib, + run_make_support_rmeta: args.run_make_support_rmeta, + runner: args.runner, + rust_randomized_layout: args.rust_randomized_layout, + rustc_path: args.rustc_path, + rustdoc_path: args.rustdoc_path, + rustfix_coverage: args.rustfix_coverage, + skip: args.skip, + src_root, + src_test_suite_root, - minicore_path: args.minicore_path, + stage0_rustc_path: args.stage0_rustc_path, + stage: args.stage, + stage_id: args.stage_id, - default_codegen_backend, - override_codegen_backend: args.override_codegen_backend, - bypass_ignore_backends: args.bypass_ignore_backends, + suite: args.suite, + supported_crate_types: OnceLock::new(), - gcc_supported_target_tuples, + sysroot_base: args.sysroot_base, + system_llvm: args.system_llvm, + target: args.target, + target_cfgs: OnceLock::new(), + target_linker: args.target_linker, + target_run_lib_path: make_absolute(args.run_lib_path), + target_rustcflags: args.target_rustcflags, + verbose: args.verbose, + verbose_run_make_subprocess_output: args.verbose_run_make_subprocess_output, wasm_proc_macros: args.wasm_proc_macros, - jobs: args.jobs, - - parallel_frontend_threads, - iteration_count, + with_rustc_debug_assertions: args.with_rustc_debug_assertions, + with_std_debug_assertions: args.with_std_debug_assertions, + with_std_remap_debuginfo: args.with_std_remap_debuginfo, + // tidy-alphabetical-end } } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index c5a631ad94589..1102f9eaaeff2 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -347,6 +347,12 @@ pub(crate) struct Config { /// - `/home/ferris/rust/build/x86_64-unknown-linux-gnu/stage0/bin/rustc` pub(crate) stage0_rustc_path: Option, + /// Path to the run-make-support .rlib file, used to build `run-make` recipes. + pub(crate) run_make_support_rlib: Option, + + /// Path to the run-make-support .rmeta file, used to build `run-make` recipes. + pub(crate) run_make_support_rmeta: Option, + /// Path to the stage 1 or higher `rustc` used to obtain target information via /// `--print=all-target-specs-json` and similar queries. /// diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index e1aaa2a03880f..862f38d97c8cf 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -76,7 +76,12 @@ impl TestCx<'_> { let tools_bin = host_build_root.join("bootstrap-tools"); let support_host_path = tools_bin.join(&self.config.host).join("release"); - let support_lib_path = support_host_path.join("librun_make_support.rlib"); + let support_lib_rlib_path = self + .config + .run_make_support_rlib + .as_ref() + .expect("run-make-support .rlib has to be passed for run-make tests"); + let support_lib_rmeta_path = self.config.run_make_support_rmeta.as_ref(); let support_lib_deps = discover_out_dirs(support_host_path.join("build")); let support_lib_deps_deps = discover_out_dirs(tools_bin.join("release").join("build")); @@ -123,17 +128,20 @@ impl TestCx<'_> { .arg("-o") .arg(&recipe_bin) // Specify library search paths for `run_make_support`. - .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap())) .args(out_dirs_to_args(support_lib_deps)) .args(out_dirs_to_args(support_lib_deps_deps)) // Provide `run_make_support` as extern prelude, so test writers don't need to write // `extern run_make_support;`. .arg("--extern") - .arg(format!("run_make_support={}", &support_lib_path)) + .arg(format!("run_make_support={}", &support_lib_rlib_path)) .arg("--edition=2024") .arg(&self.testpaths.file.join("rmake.rs")) .arg("-Cprefer-dynamic"); + if let Some(support_lib_rmeta_path) = support_lib_rmeta_path { + rustc.arg("--extern").arg(format!("run_make_support={}", &support_lib_rmeta_path)); + } + // In test code we want to be very pedantic about values being silently discarded that are // annotated with `#[must_use]`. rustc.arg("-Dunused_must_use"); diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 88880f09837ee..b91a586c3d9ca 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -63,6 +63,8 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { rustc_path: Utf8PathBuf::default(), cargo_path: Default::default(), stage0_rustc_path: Default::default(), + run_make_support_rlib: Default::default(), + run_make_support_rmeta: Default::default(), query_rustc_path: Default::default(), rustdoc_path: Default::default(), coverage_dump_path: Default::default(),