Skip to content

Commit 05c9c0e

Browse files
Modify executable checking to be more universal
This uses a dummy file to check if the filesystem being used supports the executable bit in general.
1 parent 90b1f5a commit 05c9c0e

File tree

3 files changed

+51
-12
lines changed

3 files changed

+51
-12
lines changed

src/bootstrap/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ impl Step for Tidy {
737737
let mut cmd = builder.tool_cmd(Tool::Tidy);
738738
cmd.arg(&builder.src);
739739
cmd.arg(&builder.initial_cargo);
740+
cmd.arg(&builder.out);
740741
if builder.is_verbose() {
741742
cmd.arg("--verbose");
742743
}

src/tools/tidy/src/bins.rs

+45-9
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,56 @@ use std::path::Path;
99

1010
// All files are executable on Windows, so just check on Unix.
1111
#[cfg(windows)]
12-
pub fn check(_path: &Path, _bad: &mut bool) {}
12+
pub fn check(_path: &Path, _output: &Path, _bad: &mut bool) {}
1313

1414
#[cfg(unix)]
15-
pub fn check(path: &Path, bad: &mut bool) {
15+
pub fn check(path: &Path, output: &Path, bad: &mut bool) {
1616
use std::fs;
1717
use std::os::unix::prelude::*;
1818
use std::process::{Command, Stdio};
1919

20-
if let Ok(contents) = fs::read_to_string("/proc/version") {
21-
// Probably on Windows Linux Subsystem or Docker via VirtualBox,
22-
// all files will be marked as executable, so skip checking.
23-
if contents.contains("Microsoft") || contents.contains("boot2docker") {
24-
return;
20+
fn is_executable(path: &Path) -> std::io::Result<bool> {
21+
Ok(path.metadata()?.mode() & 0o111 != 0)
22+
}
23+
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+
let mut temp_path = path.join("tidy-test-file");
36+
match fs::File::create(&temp_path).or_else(|_| {
37+
temp_path = output.join("tidy-test-file");
38+
fs::File::create(&temp_path)
39+
}) {
40+
Ok(file) => {
41+
let exec = is_executable(&temp_path).unwrap_or(false);
42+
std::mem::drop(file);
43+
std::fs::remove_file(&temp_path).expect("Deleted temp file");
44+
if exec {
45+
// If the file is executable, then we assume that this
46+
// filesystem does not track executability, so skip this check.
47+
return;
48+
}
49+
}
50+
Err(e) => {
51+
// If the directory is read-only or we otherwise don't have rights,
52+
// just don't run this check.
53+
//
54+
// 30 is the "Read-only filesystem" code at least in one CI
55+
// environment.
56+
if e.raw_os_error() == Some(30) {
57+
eprintln!("tidy: Skipping binary file check, read-only filesystem");
58+
return;
59+
}
60+
61+
panic!("unable to create temporary file `{:?}`: {:?}", temp_path, e);
2562
}
2663
}
2764

@@ -36,8 +73,7 @@ pub fn check(path: &Path, bad: &mut bool) {
3673
return;
3774
}
3875

39-
let metadata = t!(entry.metadata(), file);
40-
if metadata.mode() & 0o111 != 0 {
76+
if t!(is_executable(&file), file) {
4177
let rel_path = file.strip_prefix(path).unwrap();
4278
let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
4379
let output = Command::new("git")

src/tools/tidy/src/main.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use std::process;
1313
fn main() {
1414
let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
1515
let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
16+
let output_directory: PathBuf =
17+
env::args_os().nth(3).expect("need path to output directory").into();
1618

1719
let src_path = root_path.join("src");
1820
let library_path = root_path.join("library");
@@ -36,9 +38,9 @@ fn main() {
3638
unit_tests::check(&library_path, &mut bad);
3739

3840
// Checks that need to be done for both the compiler and std libraries.
39-
bins::check(&src_path, &mut bad);
40-
bins::check(&compiler_path, &mut bad);
41-
bins::check(&library_path, &mut bad);
41+
bins::check(&src_path, &output_directory, &mut bad);
42+
bins::check(&compiler_path, &output_directory, &mut bad);
43+
bins::check(&library_path, &output_directory, &mut bad);
4244

4345
style::check(&src_path, &mut bad);
4446
style::check(&compiler_path, &mut bad);

0 commit comments

Comments
 (0)