Skip to content

Commit 0513ba4

Browse files
committed
perform filesystem probe once before running bins checks concurrently
this avoids concurrent write attempts to the output directory
1 parent 2071040 commit 0513ba4

File tree

3 files changed

+110
-80
lines changed

3 files changed

+110
-80
lines changed

src/tools/tidy/src/bins.rs

+102-70
Original file line numberDiff line numberDiff line change
@@ -5,94 +5,126 @@
55
//! huge amount of bloat to the Git history, so it's good to just ensure we
66
//! don't do that again.
77
8-
use std::path::Path;
8+
pub use os_impl::*;
99

1010
// All files are executable on Windows, so just check on Unix.
1111
#[cfg(windows)]
12-
pub fn check(_path: &Path, _output: &Path, _bad: &mut bool) {}
12+
mod os_impl {
13+
use std::path::Path;
14+
15+
pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool {
16+
return false;
17+
}
18+
19+
pub fn check(_path: &Path, _bad: &mut bool) {}
20+
}
1321

1422
#[cfg(unix)]
15-
pub fn check(path: &Path, output: &Path, bad: &mut bool) {
23+
mod os_impl {
1624
use std::fs;
1725
use std::os::unix::prelude::*;
26+
use std::path::Path;
1827
use std::process::{Command, Stdio};
1928

29+
enum FilesystemSupport {
30+
Supported,
31+
Unsupported,
32+
ReadOnlyFs,
33+
}
34+
35+
use FilesystemSupport::*;
36+
2037
fn is_executable(path: &Path) -> std::io::Result<bool> {
2138
Ok(path.metadata()?.mode() & 0o111 != 0)
2239
}
2340

24-
// We want to avoid false positives on filesystems that do not support the
25-
// executable bit. This occurs on some versions of Window's linux subsystem,
26-
// for example.
27-
//
28-
// We try to create the temporary file first in the src directory, which is
29-
// the preferred location as it's most likely to be on the same filesystem,
30-
// and then in the output (`build`) directory if that fails. Sometimes we
31-
// see the source directory mounted as read-only which means we can't
32-
// readily create a file there to test.
33-
//
34-
// See #36706 and #74753 for context.
35-
//
36-
// We also add the thread ID to avoid threads trampling on each others files.
37-
let file_name = format!("t{}.tidy-test-file", std::thread::current().id().as_u64());
38-
let mut temp_path = path.join(&file_name);
39-
match fs::File::create(&temp_path).or_else(|_| {
40-
temp_path = output.join(&file_name);
41-
fs::File::create(&temp_path)
42-
}) {
43-
Ok(file) => {
44-
let exec = is_executable(&temp_path).unwrap_or(false);
45-
std::mem::drop(file);
46-
std::fs::remove_file(&temp_path).expect("Deleted temp file");
47-
if exec {
48-
// If the file is executable, then we assume that this
49-
// filesystem does not track executability, so skip this check.
50-
return;
51-
}
41+
pub fn check_filesystem_support(sources: &[&Path], output: &Path) -> bool {
42+
// We want to avoid false positives on filesystems that do not support the
43+
// executable bit. This occurs on some versions of Window's linux subsystem,
44+
// for example.
45+
//
46+
// We try to create the temporary file first in the src directory, which is
47+
// the preferred location as it's most likely to be on the same filesystem,
48+
// and then in the output (`build`) directory if that fails. Sometimes we
49+
// see the source directory mounted as read-only which means we can't
50+
// readily create a file there to test.
51+
//
52+
// See #36706 and #74753 for context.
53+
54+
fn check_dir(dir: &Path) -> FilesystemSupport {
55+
let path = dir.join("tidy-test-file");
56+
match fs::File::create(&path) {
57+
Ok(file) => {
58+
let exec = is_executable(&path).unwrap_or(false);
59+
std::mem::drop(file);
60+
std::fs::remove_file(&path).expect("Deleted temp file");
61+
// If the file is executable, then we assume that this
62+
// filesystem does not track executability, so skip this check.
63+
return if exec { Unsupported } else { Supported };
64+
}
65+
Err(e) => {
66+
// If the directory is read-only or we otherwise don't have rights,
67+
// just don't run this check.
68+
//
69+
// 30 is the "Read-only filesystem" code at least in one CI
70+
// environment.
71+
if e.raw_os_error() == Some(30) {
72+
eprintln!("tidy: Skipping binary file check, read-only filesystem");
73+
return ReadOnlyFs;
74+
}
75+
76+
panic!("unable to create temporary file `{:?}`: {:?}", path, e);
77+
}
78+
};
5279
}
53-
Err(e) => {
54-
// If the directory is read-only or we otherwise don't have rights,
55-
// just don't run this check.
56-
//
57-
// 30 is the "Read-only filesystem" code at least in one CI
58-
// environment.
59-
if e.raw_os_error() == Some(30) {
60-
eprintln!("tidy: Skipping binary file check, read-only filesystem");
61-
return;
62-
}
6380

64-
panic!("unable to create temporary file `{:?}`: {:?}", temp_path, e);
81+
for &source_dir in sources {
82+
match check_dir(source_dir) {
83+
Unsupported => return false,
84+
ReadOnlyFs => {
85+
return match check_dir(output) {
86+
Supported => true,
87+
_ => false,
88+
};
89+
}
90+
_ => {}
91+
}
6592
}
93+
94+
return true;
6695
}
6796

68-
super::walk_no_read(
69-
path,
70-
&mut |path| super::filter_dirs(path) || path.ends_with("src/etc"),
71-
&mut |entry| {
72-
let file = entry.path();
73-
let filename = file.file_name().unwrap().to_string_lossy();
74-
let extensions = [".py", ".sh"];
75-
if extensions.iter().any(|e| filename.ends_with(e)) {
76-
return;
77-
}
97+
#[cfg(unix)]
98+
pub fn check(path: &Path, bad: &mut bool) {
99+
crate::walk_no_read(
100+
path,
101+
&mut |path| crate::filter_dirs(path) || path.ends_with("src/etc"),
102+
&mut |entry| {
103+
let file = entry.path();
104+
let filename = file.file_name().unwrap().to_string_lossy();
105+
let extensions = [".py", ".sh"];
106+
if extensions.iter().any(|e| filename.ends_with(e)) {
107+
return;
108+
}
78109

79-
if t!(is_executable(&file), file) {
80-
let rel_path = file.strip_prefix(path).unwrap();
81-
let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
82-
let output = Command::new("git")
83-
.arg("ls-files")
84-
.arg(&git_friendly_path)
85-
.current_dir(path)
86-
.stderr(Stdio::null())
87-
.output()
88-
.unwrap_or_else(|e| {
89-
panic!("could not run git ls-files: {}", e);
90-
});
91-
let path_bytes = rel_path.as_os_str().as_bytes();
92-
if output.status.success() && output.stdout.starts_with(path_bytes) {
93-
tidy_error!(bad, "binary checked into source: {}", file.display());
110+
if t!(is_executable(&file), file) {
111+
let rel_path = file.strip_prefix(path).unwrap();
112+
let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
113+
let output = Command::new("git")
114+
.arg("ls-files")
115+
.arg(&git_friendly_path)
116+
.current_dir(path)
117+
.stderr(Stdio::null())
118+
.output()
119+
.unwrap_or_else(|e| {
120+
panic!("could not run git ls-files: {}", e);
121+
});
122+
let path_bytes = rel_path.as_os_str().as_bytes();
123+
if output.status.success() && output.stdout.starts_with(path_bytes) {
124+
tidy_error!(bad, "binary checked into source: {}", file.display());
125+
}
94126
}
95-
}
96-
},
97-
)
127+
},
128+
)
129+
}
98130
}

src/tools/tidy/src/lib.rs

-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//! to be used by tools.
55
66
#![cfg_attr(bootstrap, feature(str_split_once))]
7-
#![feature(thread_id_value)]
87

98
use std::fs::File;
109
use std::io::Read;
@@ -55,12 +54,6 @@ pub mod unit_tests;
5554
pub mod unstable_book;
5655

5756
fn filter_dirs(path: &Path) -> bool {
58-
// Filter out temporary files used by the bins module to probe the filesystem
59-
match path.extension() {
60-
Some(ext) if ext == "tidy-test-file" => return true,
61-
_ => {}
62-
}
63-
6457
let skip = [
6558
"compiler/rustc_codegen_cranelift",
6659
"src/llvm-project",

src/tools/tidy/src/main.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,14 @@ fn main() {
7575
check!(unit_tests, &compiler_path);
7676
check!(unit_tests, &library_path);
7777

78-
check!(bins, &src_path, &output_directory);
79-
check!(bins, &compiler_path, &output_directory);
80-
check!(bins, &library_path, &output_directory);
78+
if bins::check_filesystem_support(
79+
&[&src_path, &compiler_path, &library_path],
80+
&output_directory,
81+
) {
82+
check!(bins, &src_path);
83+
check!(bins, &compiler_path);
84+
check!(bins, &library_path);
85+
}
8186

8287
check!(style, &src_path);
8388
check!(style, &compiler_path);

0 commit comments

Comments
 (0)