module Agda.Utils.IO.Directory ( copyDirContent ) where import Control.Monad import Control.Monad.Writer import System.Directory import System.FilePath import Data.ByteString as BS import Paths_Agda import Agda.Utils.Functor -- | @copyDirContent src dest@ recursively copies directory @src@ onto @dest@. -- -- First, a to-do list of copy actions is created. -- Then, the to-do list is carried out. -- -- This avoids copying files we have just created again, which can happen -- if @src@ and @dest@ are not disjoint. -- (See issue #2705.) -- copyDirContent :: FilePath -> FilePath -> IO () copyDirContent src dest = mapM_ performAction =<< do (`appEndo` []) <$> execWriterT (copyDirContentDryRun src dest) -- | Action to be carried out for copying a directory recursively. -- data CopyDirAction = MkDir FilePath -- ^ Create directory if missing. | CopyFile FilePath FilePath -- ^ Copy file if changed. -- | Perform scheduled 'CopyDirAction'. -- performAction :: CopyDirAction -> IO () performAction = \case MkDir d -> createDirectoryIfMissing True d CopyFile src dest -> copyIfChanged src dest -- | @copyDirContentDryRun src dest@ creates a to-do list -- for recursively copying directory @src@ onto @dest@. -- copyDirContentDryRun :: FilePath -> FilePath -> WriterT (Endo [CopyDirAction]) IO () copyDirContentDryRun src dest = do tell $ Endo (MkDir dest :) chlds <- lift $ getDirectoryContents src forM_ chlds $ \ x -> do isDir <- lift $ doesDirectoryExist (src x) case isDir of _ | x == "." || x == ".." -> return () True -> copyDirContentDryRun (src x) (dest x) False -> tell $ Endo (CopyFile (src x) (dest x) :) -- | @copyIfChanged src dst@ makes sure that @dst@ exists -- and has the same content as @dst@. -- copyIfChanged :: FilePath -> FilePath -> IO () copyIfChanged src dst = do exist <- doesFileExist dst if not exist then copyFile src dst else do new <- BS.readFile src old <- BS.readFile dst unless (old == new) $ copyFile src dst