1 //! Tidy check to ensure that there are no binaries checked into the source tree
4 //! In the past we've accidentally checked in test binaries and such which add a
5 //! huge amount of bloat to the Git history, so it's good to just ensure we
6 //! don't do that again.
10 // All files are executable on Windows, so just check on Unix.
15 pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool {
19 pub fn check(_path: &Path, _bad: &mut bool) {}
24 use crate::walk::{filter_dirs, walk_no_read};
26 use std::os::unix::prelude::*;
28 use std::process::{Command, Stdio};
30 enum FilesystemSupport {
36 use FilesystemSupport::*;
38 fn is_executable(path: &Path) -> std::io::Result<bool> {
39 Ok(path.metadata()?.mode() & 0o111 != 0)
42 pub fn check_filesystem_support(sources: &[&Path], output: &Path) -> bool {
43 // We want to avoid false positives on filesystems that do not support the
44 // executable bit. This occurs on some versions of Window's linux subsystem,
47 // We try to create the temporary file first in the src directory, which is
48 // the preferred location as it's most likely to be on the same filesystem,
49 // and then in the output (`build`) directory if that fails. Sometimes we
50 // see the source directory mounted as read-only which means we can't
51 // readily create a file there to test.
53 // See #36706 and #74753 for context.
55 fn check_dir(dir: &Path) -> FilesystemSupport {
56 let path = dir.join("tidy-test-file");
57 match fs::File::create(&path) {
59 let exec = is_executable(&path).unwrap_or(false);
61 std::fs::remove_file(&path).expect("Deleted temp file");
62 // If the file is executable, then we assume that this
63 // filesystem does not track executability, so skip this check.
64 return if exec { Unsupported } else { Supported };
67 // If the directory is read-only or we otherwise don't have rights,
68 // just don't run this check.
70 // 30 is the "Read-only filesystem" code at least in one CI
72 if e.raw_os_error() == Some(30) {
73 eprintln!("tidy: Skipping binary file check, read-only filesystem");
77 panic!("unable to create temporary file `{:?}`: {:?}", path, e);
82 for &source_dir in sources {
83 match check_dir(source_dir) {
84 Unsupported => return false,
86 return match check_dir(output) {
99 pub fn check(path: &Path, bad: &mut bool) {
102 const ALLOWED: &[&str] = &["configure", "x"];
108 || path.ends_with("src/etc")
109 // This is a list of directories that we almost certainly
110 // don't need to walk. A future PR will likely want to
111 // remove these in favor of crate::walk_no_read using git
112 // ls-files to discover the paths we should check, which
113 // would naturally ignore all of these directories. It's
114 // also likely faster than walking the directory tree
115 // directly (since git is just reading from a couple files
116 // to produce the results).
117 || path.ends_with("target")
118 || path.ends_with("build")
119 || path.ends_with(".git")
122 let file = entry.path();
123 let extension = file.extension();
124 let scripts = ["py", "sh", "ps1"];
125 if scripts.into_iter().any(|e| extension == Some(OsStr::new(e))) {
129 if t!(is_executable(&file), file) {
130 let rel_path = file.strip_prefix(path).unwrap();
131 let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
133 if ALLOWED.contains(&git_friendly_path.as_str()) {
137 let output = Command::new("git")
139 .arg(&git_friendly_path)
141 .stderr(Stdio::null())
143 .unwrap_or_else(|e| {
144 panic!("could not run git ls-files: {e}");
146 let path_bytes = rel_path.as_os_str().as_bytes();
147 if output.status.success() && output.stdout.starts_with(path_bytes) {
148 tidy_error!(bad, "binary checked into source: {}", file.display());