{-# LANGUAGE CPP #-} -- | Utility functions specific to 'directory' tests module TestUtils ( copyPathRecursive , modifyPermissions , symlinkOrCopy , supportsSymlinks ) where import Prelude () import System.Directory.Internal.Prelude import System.Directory import System.FilePath ((), normalise, takeDirectory) #if defined(mingw32_HOST_OS) import System.Directory.Internal (win32_getFinalPathNameByHandle) import qualified System.Win32 as Win32 #endif -- | @'copyPathRecursive' path@ copies an existing file or directory at -- /path/ together with its contents and subdirectories. -- -- Warning: mostly untested and might not handle symlinks correctly. copyPathRecursive :: FilePath -> FilePath -> IO () copyPathRecursive source dest = (`ioeSetLocation` "copyPathRecursive") `modifyIOError` do dirExists <- doesDirectoryExist source if dirExists then do contents <- listDirectory source createDirectory dest mapM_ (uncurry copyPathRecursive) [(source x, dest x) | x <- contents] else copyFile source dest modifyPermissions :: FilePath -> (Permissions -> Permissions) -> IO () modifyPermissions path modify = do permissions <- getPermissions path setPermissions path (modify permissions) -- | On Windows, the handler is called if symbolic links are unsupported or -- the user lacks the necessary privileges to create them. On other -- platforms, the handler is never run. handleSymlinkUnavail :: IO a -- ^ handler -> IO a -- ^ arbitrary action -> IO a handleSymlinkUnavail _handler action = action #if defined(mingw32_HOST_OS) `catchIOError` \ e -> case ioeGetErrorType e of UnsupportedOperation -> _handler _ | isIllegalOperation e || isPermissionError e -> _handler _ -> ioError e #endif -- | Create a symbolic link. On Windows, this falls back to copying if -- forbidden by Group Policy or is not supported. On other platforms, there -- is no fallback. Also, automatically detect if the source is a file or a -- directory and create the appropriate type of link. symlinkOrCopy :: FilePath -> FilePath -> IO () symlinkOrCopy target link = do let fullTarget = takeDirectory link target handleSymlinkUnavail (copyPathRecursive fullTarget link) $ do isDir <- doesDirectoryExist fullTarget (if isDir then createDirectoryLink else createFileLink) (normalise target) link supportsSymlinks :: IO Bool supportsSymlinks = do canCreate <- supportsLinkCreation canDeref <- supportsLinkDeref return (canCreate && canDeref) -- | On Windows, test if symbolic link creation is supported and the user has -- the necessary privileges to create them. On other platforms, this always -- returns 'True'. supportsLinkCreation :: IO Bool supportsLinkCreation = do let path = "_symlink_test.tmp" isSupported <- handleSymlinkUnavail (return False) $ do True <$ createFileLink path path when isSupported $ do removeFile path return isSupported supportsLinkDeref :: IO Bool supportsLinkDeref = do #if defined(mingw32_HOST_OS) True <$ win32_getFinalPathNameByHandle Win32.nullHANDLE 0 `catchIOError` \ e -> case ioeGetErrorType e of UnsupportedOperation -> return False _ -> return True #else return True #endif