]> git.lizzy.rs Git - rust.git/commitdiff
Modify executable checking to be more universal
authorMark Rousskov <mark.simulacrum@gmail.com>
Fri, 11 Sep 2020 17:10:15 +0000 (13:10 -0400)
committerMark Rousskov <mark.simulacrum@gmail.com>
Tue, 15 Sep 2020 14:00:11 +0000 (10:00 -0400)
This uses a dummy file to check if the filesystem being used supports the
executable bit in general.

src/bootstrap/test.rs
src/tools/tidy/src/bins.rs
src/tools/tidy/src/main.rs

index 045dda2d4cb4cbbc5da6631bda9dc4616e9c670f..82a4e415b8d860d0416a35d1f70799d4ad4a19c5 100644 (file)
@@ -737,6 +737,7 @@ fn run(self, builder: &Builder<'_>) {
         let mut cmd = builder.tool_cmd(Tool::Tidy);
         cmd.arg(&builder.src);
         cmd.arg(&builder.initial_cargo);
+        cmd.arg(&builder.out);
         if builder.is_verbose() {
             cmd.arg("--verbose");
         }
index 589be26dc27edb97f4287064eb7e8cc684e60b82..62cfa85577f983e2155ffc93b21272ea09f9912f 100644 (file)
@@ -9,19 +9,56 @@
 
 // All files are executable on Windows, so just check on Unix.
 #[cfg(windows)]
-pub fn check(_path: &Path, _bad: &mut bool) {}
+pub fn check(_path: &Path, _output: &Path, _bad: &mut bool) {}
 
 #[cfg(unix)]
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, output: &Path, bad: &mut bool) {
     use std::fs;
     use std::os::unix::prelude::*;
     use std::process::{Command, Stdio};
 
-    if let Ok(contents) = fs::read_to_string("/proc/version") {
-        // Probably on Windows Linux Subsystem or Docker via VirtualBox,
-        // all files will be marked as executable, so skip checking.
-        if contents.contains("Microsoft") || contents.contains("boot2docker") {
-            return;
+    fn is_executable(path: &Path) -> std::io::Result<bool> {
+        Ok(path.metadata()?.mode() & 0o111 != 0)
+    }
+
+    // We want to avoid false positives on filesystems that do not support the
+    // executable bit. This occurs on some versions of Window's linux subsystem,
+    // for example.
+    //
+    // We try to create the temporary file first in the src directory, which is
+    // the preferred location as it's most likely to be on the same filesystem,
+    // and then in the output (`build`) directory if that fails. Sometimes we
+    // see the source directory mounted as read-only which means we can't
+    // readily create a file there to test.
+    //
+    // See #36706 and #74753 for context.
+    let mut temp_path = path.join("tidy-test-file");
+    match fs::File::create(&temp_path).or_else(|_| {
+        temp_path = output.join("tidy-test-file");
+        fs::File::create(&temp_path)
+    }) {
+        Ok(file) => {
+            let exec = is_executable(&temp_path).unwrap_or(false);
+            std::mem::drop(file);
+            std::fs::remove_file(&temp_path).expect("Deleted temp file");
+            if exec {
+                // If the file is executable, then we assume that this
+                // filesystem does not track executability, so skip this check.
+                return;
+            }
+        }
+        Err(e) => {
+            // If the directory is read-only or we otherwise don't have rights,
+            // just don't run this check.
+            //
+            // 30 is the "Read-only filesystem" code at least in one CI
+            //    environment.
+            if e.raw_os_error() == Some(30) {
+                eprintln!("tidy: Skipping binary file check, read-only filesystem");
+                return;
+            }
+
+            panic!("unable to create temporary file `{:?}`: {:?}", temp_path, e);
         }
     }
 
@@ -36,8 +73,7 @@ pub fn check(path: &Path, bad: &mut bool) {
                 return;
             }
 
-            let metadata = t!(entry.metadata(), file);
-            if metadata.mode() & 0o111 != 0 {
+            if t!(is_executable(&file), file) {
                 let rel_path = file.strip_prefix(path).unwrap();
                 let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
                 let output = Command::new("git")
index 36c9e58eb9a8787615ca2111f47fcfdb2c6db577..e1525f8e1bf238b276a3f87f711b002fb6142bef 100644 (file)
@@ -13,6 +13,8 @@
 fn main() {
     let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
     let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
+    let output_directory: PathBuf =
+        env::args_os().nth(3).expect("need path to output directory").into();
 
     let src_path = root_path.join("src");
     let library_path = root_path.join("library");
@@ -36,9 +38,9 @@ fn main() {
     unit_tests::check(&library_path, &mut bad);
 
     // Checks that need to be done for both the compiler and std libraries.
-    bins::check(&src_path, &mut bad);
-    bins::check(&compiler_path, &mut bad);
-    bins::check(&library_path, &mut bad);
+    bins::check(&src_path, &output_directory, &mut bad);
+    bins::check(&compiler_path, &output_directory, &mut bad);
+    bins::check(&library_path, &output_directory, &mut bad);
 
     style::check(&src_path, &mut bad);
     style::check(&compiler_path, &mut bad);