Rollup merge of #140086 - ChrisDenton:backoff, r=petrochenkov

If creating a temporary directory fails with permission denied then retry with backoff

On Windows, if creating a temporary directory fails with permission denied then use a retry/backoff loop. This hopefully fixes a recuring error in our CI.

cc ```@jieyouxu,``` https://github.com/rust-lang/rust/issues/133959
This commit is contained in:
Matthias Krüger
2025-04-26 07:13:07 +02:00
committed by GitHub
7 changed files with 54 additions and 11 deletions

View File

@@ -3699,6 +3699,9 @@ dependencies = [
[[package]] [[package]]
name = "rustc_fs_util" name = "rustc_fs_util"
version = "0.0.0" version = "0.0.0"
dependencies = [
"tempfile",
]
[[package]] [[package]]
name = "rustc_graphviz" name = "rustc_graphviz"
@@ -4011,7 +4014,6 @@ dependencies = [
"rustc_session", "rustc_session",
"rustc_span", "rustc_span",
"rustc_target", "rustc_target",
"tempfile",
"tracing", "tracing",
] ]

View File

@@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile;
use object::read::macho::FatArch; use object::read::macho::FatArch;
use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::memmap::Mmap; use rustc_data_structures::memmap::Mmap;
use rustc_fs_util::TempDirBuilder;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::Symbol; use rustc_span::Symbol;
use tempfile::Builder as TempFileBuilder;
use tracing::trace; use tracing::trace;
use super::metadata::search_for_section; use super::metadata::search_for_section;
@@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> {
// it creates. We need it to be the default mode for back compat reasons however. (See // it creates. We need it to be the default mode for back compat reasons however. (See
// #107495) To handle this we are telling tempfile to create a temporary directory instead // #107495) To handle this we are telling tempfile to create a temporary directory instead
// and then inside this directory create a file using File::create. // and then inside this directory create a file using File::create.
let archive_tmpdir = TempFileBuilder::new() let archive_tmpdir = TempDirBuilder::new()
.suffix(".temp-archive") .suffix(".temp-archive")
.tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) .tempdir_in(output.parent().unwrap_or_else(|| Path::new("")))
.map_err(|err| { .map_err(|err| {

View File

@@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::memmap::Mmap; use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_macros::LintDiagnostic; use rustc_macros::LintDiagnostic;
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
@@ -48,7 +48,6 @@ use rustc_target::spec::{
LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel,
SanitizerSet, SplitDebuginfo, SanitizerSet, SplitDebuginfo,
}; };
use tempfile::Builder as TempFileBuilder;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -100,7 +99,7 @@ pub fn link_binary(
}); });
if outputs.outputs.should_link() { if outputs.outputs.should_link() {
let tmpdir = TempFileBuilder::new() let tmpdir = TempDirBuilder::new()
.prefix("rustc") .prefix("rustc")
.tempdir() .tempdir()
.unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error }));

View File

@@ -5,4 +5,5 @@ edition = "2024"
[dependencies] [dependencies]
# tidy-alphabetical-start # tidy-alphabetical-start
tempfile = "3.7.1"
# tidy-alphabetical-end # tidy-alphabetical-end

View File

@@ -1,6 +1,8 @@
use std::ffi::CString; use std::ffi::{CString, OsStr};
use std::path::{Path, PathBuf, absolute}; use std::path::{Path, PathBuf, absolute};
use std::{fs, io}; use std::{env, fs, io};
use tempfile::TempDir;
// Unfortunately, on windows, it looks like msvcrt.dll is silently translating // Unfortunately, on windows, it looks like msvcrt.dll is silently translating
// verbatim paths under the hood to non-verbatim paths! This manifests itself as // verbatim paths under the hood to non-verbatim paths! This manifests itself as
@@ -102,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString {
pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
fs::canonicalize(&path).or_else(|_| absolute(&path)) fs::canonicalize(&path).or_else(|_| absolute(&path))
} }
pub struct TempDirBuilder<'a, 'b> {
builder: tempfile::Builder<'a, 'b>,
}
impl<'a, 'b> TempDirBuilder<'a, 'b> {
pub fn new() -> Self {
Self { builder: tempfile::Builder::new() }
}
pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
self.builder.prefix(prefix);
self
}
pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
self.builder.suffix(suffix);
self
}
pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
let dir = dir.as_ref();
// On Windows in CI, we had been getting fairly frequent "Access is denied"
// errors when creating temporary directories.
// So this implements a simple retry with backoff loop.
#[cfg(windows)]
for wait in 1..11 {
match self.builder.tempdir_in(dir) {
Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {}
t => return t,
}
std::thread::sleep(std::time::Duration::from_millis(1 << wait));
}
self.builder.tempdir_in(dir)
}
pub fn tempdir(&self) -> io::Result<TempDir> {
self.tempdir_in(env::temp_dir())
}
}

View File

@@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" } rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" } rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" } rustc_target = { path = "../rustc_target" }
tempfile = "3.2"
tracing = "0.1" tracing = "0.1"
# tidy-alphabetical-end # tidy-alphabetical-end

View File

@@ -2,11 +2,11 @@ use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io};
use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_fs_util::TempDirBuilder;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::config::{CrateType, OutFileName, OutputType};
use rustc_session::output::filename_for_metadata; use rustc_session::output::filename_for_metadata;
use rustc_session::{MetadataKind, Session}; use rustc_session::{MetadataKind, Session};
use tempfile::Builder as TempFileBuilder;
use crate::errors::{ use crate::errors::{
BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile,
@@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
// final destination, with an `fs::rename` call. In order for the rename to // final destination, with an `fs::rename` call. In order for the rename to
// always succeed, the temporary file needs to be on the same filesystem, // always succeed, the temporary file needs to be on the same filesystem,
// which is why we create it inside the output directory specifically. // which is why we create it inside the output directory specifically.
let metadata_tmpdir = TempFileBuilder::new() let metadata_tmpdir = TempDirBuilder::new()
.prefix("rmeta") .prefix("rmeta")
.tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
.unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));