git-annex/Utility/DirWatcher/FSEvents.hs
Joey Hess 0a4479b8ec
Avoid backtraces on expected failures when built with ghc 8; only use backtraces for unexpected errors.
ghc 8 added backtraces on uncaught errors. This is great, but git-annex was
using error in many places for a error message targeted at the user, in
some known problem case. A backtrace only confuses such a message, so omit it.

Notably, commands like git annex drop that failed due to eg, numcopies,
used to use error, so had a backtrace.

This commit was sponsored by Ethan Aubin.
2016-11-15 21:29:54 -04:00

96 lines
3.1 KiB
Haskell

{- FSEvents interface
-
- Copyright 2012 Joey Hess <id@joeyh.name>
-
- License: BSD-2-clause
-}
module Utility.DirWatcher.FSEvents where
import Common hiding (isDirectory)
import Utility.DirWatcher.Types
import System.OSX.FSEvents
import qualified System.Posix.Files as Files
import Data.Bits ((.&.))
watchDir :: FilePath -> (FilePath -> Bool) -> Bool -> WatchHooks -> IO EventStream
watchDir dir ignored scanevents hooks = do
unlessM fileLevelEventsSupported $
giveup "Need at least OSX 10.7.0 for file-level FSEvents"
scan dir
eventStreamCreate [dir] 1.0 True True True dispatch
where
dispatch evt
| ignoredPath ignored (eventPath evt) = noop
| otherwise = do
{- More than one flag may be set, if events occurred
- close together.
-
- Order is important..
- If a file is added and then deleted, we'll see it's
- not present, and addHook won't run.
- OTOH, if a file is deleted and then re-added,
- the delHook will run first, followed by the addHook.
-}
when (hasflag eventFlagItemRemoved) $
if hasflag eventFlagItemIsDir
then runhook delDirHook Nothing
else runhook delHook Nothing
when (hasflag eventFlagItemCreated) $
maybe noop handleadd =<< getstatus (eventPath evt)
{- When a file or dir is renamed, a rename event is
- received for both its old and its new name. -}
when (hasflag eventFlagItemRenamed) $
if hasflag eventFlagItemIsDir
then ifM (doesDirectoryExist $ eventPath evt)
( scan $ eventPath evt
, runhook delDirHook Nothing
)
else maybe (runhook delHook Nothing) handleadd
=<< getstatus (eventPath evt)
{- Add hooks are run when a file is modified for
- compatability with INotify, which calls the add
- hook when a file is closed, and so tends to call
- both add and modify for file modifications. -}
when (hasflag eventFlagItemModified && not (hasflag eventFlagItemIsDir)) $ do
ms <- getstatus $ eventPath evt
maybe noop handleadd ms
runhook modifyHook ms
where
hasflag f = eventFlags evt .&. f /= 0
runhook h s = maybe noop (\a -> a (eventPath evt) s) (h hooks)
handleadd s
| Files.isSymbolicLink s = runhook addSymlinkHook $ Just s
| Files.isRegularFile s = runhook addHook $ Just s
| otherwise = noop
scan d = unless (ignoredPath ignored d) $
-- Do not follow symlinks when scanning.
-- This mirrors the inotify startup scan behavior.
mapM_ go =<< dirContentsRecursiveSkipping (const False) False d
where
go f
| ignoredPath ignored f = noop
| otherwise = do
ms <- getstatus f
case ms of
Nothing -> noop
Just s
| Files.isSymbolicLink s ->
when scanevents $
runhook addSymlinkHook ms
| Files.isRegularFile s ->
when scanevents $
runhook addHook ms
| otherwise ->
noop
where
runhook h s = maybe noop (\a -> a f s) (h hooks)
getstatus = catchMaybeIO . getSymbolicLinkStatus
{- Check each component of the path to see if it's ignored. -}
ignoredPath :: (FilePath -> Bool) -> FilePath -> Bool
ignoredPath ignored = any ignored . map dropTrailingPathSeparator . splitPath