Skip to content

Commit

Permalink
Merge pull request #2157 from ehuss/macos-notify-copy
Browse files Browse the repository at this point in the history
Add workaround for macOS notify problem.
  • Loading branch information
ehuss committed Aug 5, 2023
2 parents 57b487e + c903cc8 commit 7849d55
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -37,7 +37,7 @@ toml = "0.5.11" # Do not update, see https://github.com/rust-lang/mdBook/issues/
topological-sort = "0.2.2"

# Watch feature
notify = { version = "6.0.1", optional = true, features = ["macos_kqueue"] }
notify = { version = "6.0.1", optional = true }
notify-debouncer-mini = { version = "0.3.0", optional = true }
ignore = { version = "0.4.20", optional = true }

Expand Down
58 changes: 57 additions & 1 deletion src/utils/fs.rs
Expand Up @@ -166,7 +166,7 @@ pub fn copy_files_except_ext(
.expect("a file should have a file name...")
)
);
fs::copy(
copy(
entry.path(),
&to.join(
entry
Expand All @@ -180,6 +180,62 @@ pub fn copy_files_except_ext(
Ok(())
}

/// Copies a file.
fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {
let from = from.as_ref();
let to = to.as_ref();
return copy_inner(from, to)
.with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()));

// This is a workaround for an issue with the macOS file watcher.
// Rust's `std::fs::copy` function uses `fclonefileat`, which creates
// clones on APFS. Unfortunately fs events seem to trigger on both
// sides of the clone, and there doesn't seem to be a way to differentiate
// which side it is.
// https://github.com/notify-rs/notify/issues/465#issuecomment-1657261035
// contains more information.
//
// This is essentially a copy of the simple copy code path in Rust's
// standard library.
#[cfg(target_os = "macos")]
fn copy_inner(from: &Path, to: &Path) -> Result<()> {
use std::fs::OpenOptions;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};

let mut reader = File::open(from)?;
let metadata = reader.metadata()?;
if !metadata.is_file() {
anyhow::bail!(
"expected a file, `{}` appears to be {:?}",
from.display(),
metadata.file_type()
);
}
let perm = metadata.permissions();
let mut writer = OpenOptions::new()
.mode(perm.mode())
.write(true)
.create(true)
.truncate(true)
.open(to)?;
let writer_metadata = writer.metadata()?;
if writer_metadata.is_file() {
// Set the correct file permissions, in case the file already existed.
// Don't set the permissions on already existing non-files like
// pipes/FIFOs or device nodes.
writer.set_permissions(perm)?;
}
std::io::copy(&mut reader, &mut writer)?;
Ok(())
}

#[cfg(not(target_os = "macos"))]
fn copy_inner(from: &Path, to: &Path) -> Result<()> {
fs::copy(from, to)?;
Ok(())
}
}

pub fn get_404_output_file(input_404: &Option<String>) -> String {
input_404
.as_ref()
Expand Down

0 comments on commit 7849d55

Please sign in to comment.