Refactor hardlink fallback to use an enum (#401)

Makes an invalid state unrepresentable (`first_try_hard_linking = true`,
`use_copy_fallback` = true`).
This commit is contained in:
Charlie Marsh 2023-11-10 12:18:51 -08:00 committed by GitHub
parent ff4d079dc9
commit 56a4b51eb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -367,14 +367,20 @@ fn hardlink_wheel_files(
site_packages: impl AsRef<Path>, site_packages: impl AsRef<Path>,
wheel: impl AsRef<Path>, wheel: impl AsRef<Path>,
) -> Result<usize, Error> { ) -> Result<usize, Error> {
let mut count = 0usize;
// Hard linking might not be supported but we (afaik) can't detect this ahead of time, so we'll // Hard linking might not be supported but we (afaik) can't detect this ahead of time, so we'll
// try hard linking the first file, if this succeeds we'll know later hard linking errors are // try hard linking the first file, if this succeeds we'll know later hard linking errors are
// not due to lack of os/fs support, if it fails we'll switch to copying for the rest of the // not due to lack of os/fs support, if it fails we'll switch to copying for the rest of the
// install // install
let mut first_try_hard_linking = true; #[derive(Debug, Default, Clone, Copy)]
let mut use_copy_fallback = false; enum Attempt {
#[default]
Initial,
Subsequent,
UseCopyFallback,
}
let mut attempt = Attempt::default();
let mut count = 0usize;
// Walk over the directory. // Walk over the directory.
for entry in walkdir::WalkDir::new(&wheel) { for entry in walkdir::WalkDir::new(&wheel) {
@ -387,23 +393,30 @@ fn hardlink_wheel_files(
continue; continue;
} }
// Hardlink the file, unless it's the `RECORD` file, which we modify during installation. // The `RECORD` file is modified during installation, so we copy it instead of hard-linking.
if entry.path().ends_with("RECORD") { if entry.path().ends_with("RECORD") {
fs::copy(entry.path(), &out_path)?; fs::copy(entry.path(), &out_path)?;
} else if use_copy_fallback { count += 1;
fs::copy(entry.path(), &out_path)?; continue;
} else { }
let hard_link_result = fs::hard_link(entry.path(), &out_path);
// Once https://github.com/rust-lang/rust/issues/86442 is stable, use that // Fallback to copying if hardlinks aren't supported for this installation.
if let Err(err) = hard_link_result { match attempt {
if first_try_hard_linking { Attempt::Initial => {
// Once https://github.com/rust-lang/rust/issues/86442 is stable, use that
if fs::hard_link(entry.path(), &out_path).is_err() {
fs::copy(entry.path(), &out_path)?; fs::copy(entry.path(), &out_path)?;
use_copy_fallback = true; attempt = Attempt::UseCopyFallback;
} else { } else {
return Err(err.into()); attempt = Attempt::Subsequent;
} }
} }
first_try_hard_linking = false; Attempt::Subsequent => {
fs::hard_link(entry.path(), &out_path)?;
}
Attempt::UseCopyFallback => {
fs::copy(entry.path(), &out_path)?;
}
} }
count += 1; count += 1;