Skip to content

Commit 2147410

Browse files
committed
Retry if creating temp fails with access denied
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.
1 parent 077cedc commit 2147410

File tree

7 files changed

+55
-10
lines changed

7 files changed

+55
-10
lines changed

Cargo.lock

+3-1
Original file line numberDiff line numberDiff line change
@@ -3740,6 +3740,9 @@ dependencies = [
37403740
[[package]]
37413741
name = "rustc_fs_util"
37423742
version = "0.0.0"
3743+
dependencies = [
3744+
"tempfile",
3745+
]
37433746

37443747
[[package]]
37453748
name = "rustc_graphviz"
@@ -4052,7 +4055,6 @@ dependencies = [
40524055
"rustc_session",
40534056
"rustc_span",
40544057
"rustc_target",
4055-
"tempfile",
40564058
"tracing",
40574059
]
40584060

compiler/rustc_codegen_ssa/src/back/archive.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile;
1313
use object::read::macho::FatArch;
1414
use rustc_data_structures::fx::FxIndexSet;
1515
use rustc_data_structures::memmap::Mmap;
16+
use rustc_fs_util::TempDirBuilder;
1617
use rustc_session::Session;
1718
use rustc_span::Symbol;
18-
use tempfile::Builder as TempFileBuilder;
1919
use tracing::trace;
2020

2121
use super::metadata::search_for_section;
@@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> {
501501
// it creates. We need it to be the default mode for back compat reasons however. (See
502502
// #107495) To handle this we are telling tempfile to create a temporary directory instead
503503
// and then inside this directory create a file using File::create.
504-
let archive_tmpdir = TempFileBuilder::new()
504+
let archive_tmpdir = TempDirBuilder::new()
505505
.suffix(".temp-archive")
506506
.tempdir_in(output.parent().unwrap_or_else(|| Path::new("")))
507507
.map_err(|err| {

compiler/rustc_codegen_ssa/src/back/link.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet;
1818
use rustc_data_structures::memmap::Mmap;
1919
use rustc_data_structures::temp_dir::MaybeTempDir;
2020
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
21-
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
21+
use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
2222
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
2323
use rustc_macros::LintDiagnostic;
2424
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
@@ -48,7 +48,6 @@ use rustc_target::spec::{
4848
LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel,
4949
SanitizerSet, SplitDebuginfo,
5050
};
51-
use tempfile::Builder as TempFileBuilder;
5251
use tracing::{debug, info, warn};
5352

5453
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -100,7 +99,7 @@ pub fn link_binary(
10099
});
101100

102101
if outputs.outputs.should_link() {
103-
let tmpdir = TempFileBuilder::new()
102+
let tmpdir = TempDirBuilder::new()
104103
.prefix("rustc")
105104
.tempdir()
106105
.unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error }));

compiler/rustc_fs_util/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ edition = "2024"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
tempfile = "3.7.1"
89
# tidy-alphabetical-end

compiler/rustc_fs_util/src/lib.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
use std::ffi::CString;
1+
use std::ffi::{CString, OsStr};
22
use std::path::{Path, PathBuf, absolute};
33
use std::{fs, io};
44

5+
use tempfile::TempDir;
6+
57
// Unfortunately, on windows, it looks like msvcrt.dll is silently translating
68
// verbatim paths under the hood to non-verbatim paths! This manifests itself as
79
// gcc looking like it cannot accept paths of the form `\\?\C:\...`, but the
@@ -102,3 +104,45 @@ pub fn path_to_c_string(p: &Path) -> CString {
102104
pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
103105
fs::canonicalize(&path).or_else(|_| absolute(&path))
104106
}
107+
108+
pub struct TempDirBuilder<'a, 'b> {
109+
builder: tempfile::Builder<'a, 'b>,
110+
}
111+
112+
impl<'a, 'b> TempDirBuilder<'a, 'b> {
113+
pub fn new() -> Self {
114+
let mut builder = tempfile::Builder::new();
115+
builder.rand_bytes(8);
116+
Self { builder }
117+
}
118+
119+
pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
120+
self.builder.prefix(prefix);
121+
self
122+
}
123+
124+
pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
125+
self.builder.suffix(suffix);
126+
self
127+
}
128+
129+
pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
130+
let dir = dir.as_ref();
131+
// On Windows in CI, we had been getting fairly frequent "Access is denied"
132+
// errors when creating temporary directories.
133+
// So this implements a simple retry with backoff loop.
134+
#[cfg(windows)]
135+
for wait in 1..11 {
136+
match self.builder.tempdir_in(dir) {
137+
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {}
138+
t => return t,
139+
}
140+
std::thread::sleep(std::time::Duration::from_millis(1 << wait));
141+
}
142+
self.builder.tempdir_in(dir)
143+
}
144+
145+
pub fn tempdir(&self) -> io::Result<TempDir> {
146+
self.builder.tempdir_in(std::env::temp_dir())
147+
}
148+
}

compiler/rustc_metadata/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" }
2626
rustc_session = { path = "../rustc_session" }
2727
rustc_span = { path = "../rustc_span" }
2828
rustc_target = { path = "../rustc_target" }
29-
tempfile = "3.2"
3029
tracing = "0.1"
3130
# tidy-alphabetical-end
3231

compiler/rustc_metadata/src/fs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ use std::path::{Path, PathBuf};
22
use std::{fs, io};
33

44
use rustc_data_structures::temp_dir::MaybeTempDir;
5+
use rustc_fs_util::TempDirBuilder;
56
use rustc_middle::ty::TyCtxt;
67
use rustc_session::config::{CrateType, OutFileName, OutputType};
78
use rustc_session::output::filename_for_metadata;
89
use rustc_session::{MetadataKind, Session};
9-
use tempfile::Builder as TempFileBuilder;
1010

1111
use crate::errors::{
1212
BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile,
@@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
4545
// final destination, with an `fs::rename` call. In order for the rename to
4646
// always succeed, the temporary file needs to be on the same filesystem,
4747
// which is why we create it inside the output directory specifically.
48-
let metadata_tmpdir = TempFileBuilder::new()
48+
let metadata_tmpdir = TempDirBuilder::new()
4949
.prefix("rmeta")
5050
.tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new("")))
5151
.unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err }));

0 commit comments

Comments
 (0)