@@ -166,7 +166,7 @@ pub fn copy_files_except_ext(
166166 . expect( "a file should have a file name..." )
167167 )
168168 ) ;
169- fs :: copy (
169+ copy (
170170 entry. path ( ) ,
171171 & to. join (
172172 entry
@@ -180,6 +180,62 @@ pub fn copy_files_except_ext(
180180 Ok ( ( ) )
181181}
182182
183+ /// Copies a file.
184+ fn copy < P : AsRef < Path > , Q : AsRef < Path > > ( from : P , to : Q ) -> Result < ( ) > {
185+ let from = from. as_ref ( ) ;
186+ let to = to. as_ref ( ) ;
187+ return copy_inner ( from, to)
188+ . with_context ( || format ! ( "failed to copy `{}` to `{}`" , from. display( ) , to. display( ) ) ) ;
189+
190+ // This is a workaround for an issue with the macOS file watcher.
191+ // Rust's `std::fs::copy` function uses `fclonefileat`, which creates
192+ // clones on APFS. Unfortunately fs events seem to trigger on both
193+ // sides of the clone, and there doesn't seem to be a way to differentiate
194+ // which side it is.
195+ // https://github.com/notify-rs/notify/issues/465#issuecomment-1657261035
196+ // contains more information.
197+ //
198+ // This is essentially a copy of the simple copy code path in Rust's
199+ // standard library.
200+ #[ cfg( target_os = "macos" ) ]
201+ fn copy_inner ( from : & Path , to : & Path ) -> Result < ( ) > {
202+ use std:: fs:: OpenOptions ;
203+ use std:: os:: unix:: fs:: { OpenOptionsExt , PermissionsExt } ;
204+
205+ let mut reader = File :: open ( from) ?;
206+ let metadata = reader. metadata ( ) ?;
207+ if !metadata. is_file ( ) {
208+ anyhow:: bail!(
209+ "expected a file, `{}` appears to be {:?}" ,
210+ from. display( ) ,
211+ metadata. file_type( )
212+ ) ;
213+ }
214+ let perm = metadata. permissions ( ) ;
215+ let mut writer = OpenOptions :: new ( )
216+ . mode ( perm. mode ( ) )
217+ . write ( true )
218+ . create ( true )
219+ . truncate ( true )
220+ . open ( to) ?;
221+ let writer_metadata = writer. metadata ( ) ?;
222+ if writer_metadata. is_file ( ) {
223+ // Set the correct file permissions, in case the file already existed.
224+ // Don't set the permissions on already existing non-files like
225+ // pipes/FIFOs or device nodes.
226+ writer. set_permissions ( perm) ?;
227+ }
228+ std:: io:: copy ( & mut reader, & mut writer) ?;
229+ Ok ( ( ) )
230+ }
231+
232+ #[ cfg( not( target_os = "macos" ) ) ]
233+ fn copy_inner ( from : & Path , to : & Path ) -> Result < ( ) > {
234+ fs:: copy ( from, to) ?;
235+ Ok ( ( ) )
236+ }
237+ }
238+
183239pub fn get_404_output_file ( input_404 : & Option < String > ) -> String {
184240 input_404
185241 . as_ref ( )
0 commit comments