    {-# LANGUAGE CPP #-}

    module Development.Shake.Internal.History.Symlink(
        copyFileLink,
        createLinkMaybe
        ) where

    import Control.Monad.Extra
    import General.Extra
    import System.Directory
    import System.FilePath

    #ifdef mingw32_HOST_OS
    import Foreign.Ptr
    import Foreign.C.String
    #else
    import System.Posix.Files(createLink)
    #endif

    createLinkMaybe :: FilePath -> FilePath -> IO (Maybe String)

    #ifdef mingw32_HOST_OS

    #ifdef x86_64_HOST_ARCH
    #define CALLCONV ccall
    #else
    #define CALLCONV stdcall
    #endif

    foreign import CALLCONV unsafe "Windows.h CreateHardLinkW " c_CreateHardLinkW :: CWString -> CWString -> Ptr () -> IO Bool

    createLinkMaybe from to = withCWString from $ \cfrom -> withCWString to $ \cto -> do
        res <- c_CreateHardLinkW cto cfrom nullPtr
        pure $ if res then Nothing else Just "CreateHardLink failed."

    #else

    createLinkMaybe :: FilePath -> FilePath -> IO (Maybe FilePath)
    createLinkMaybe FilePath
    from FilePath
    to = (IOException -> IO (Maybe FilePath))
    -> IO (Maybe FilePath) -> IO (Maybe FilePath)
    forall a. (IOException -> IO a) -> IO a -> IO a
    handleIO (Maybe FilePath -> IO (Maybe FilePath)
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (Maybe FilePath -> IO (Maybe FilePath))
    -> (IOException -> Maybe FilePath)
    -> IOException
    -> IO (Maybe FilePath)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FilePath -> Maybe FilePath
    forall a. a -> Maybe a
    Just (FilePath -> Maybe FilePath)
    -> (IOException -> FilePath) -> IOException -> Maybe FilePath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IOException -> FilePath
    forall a. Show a => a -> FilePath
    show) (IO (Maybe FilePath) -> IO (Maybe FilePath))
    -> IO (Maybe FilePath) -> IO (Maybe FilePath)
    forall a b. (a -> b) -> a -> b
    $ FilePath -> FilePath -> IO ()
    createLink FilePath
    from FilePath
    to IO () -> IO (Maybe FilePath) -> IO (Maybe FilePath)
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Maybe FilePath -> IO (Maybe FilePath)
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure Maybe FilePath
    forall a. Maybe a
    Nothing

    #endif

    copyFileLink :: Bool -> FilePath -> FilePath -> IO ()
    copyFileLink :: Bool -> FilePath -> FilePath -> IO ()
    copyFileLink Bool
    useSymlink FilePath
    from FilePath
    to = do
        FilePath -> IO ()
    createDirectoryRecursive (FilePath -> IO ()) -> FilePath -> IO ()
    forall a b. (a -> b) -> a -> b
    $ FilePath -> FilePath
    takeDirectory FilePath
    to
        FilePath -> IO ()
    removeFile_ FilePath
    to
        if Bool -> Bool
    not Bool
    useSymlink then FilePath -> FilePath -> IO ()
    copyFile FilePath
    from FilePath
    to else do
            b <- FilePath -> FilePath -> IO (Maybe FilePath)
    createLinkMaybe FilePath
    from FilePath
    to
            whenJust b $ \FilePath
    _ ->
                FilePath -> FilePath -> IO ()
    copyFile FilePath
    from FilePath
    to
            -- making files read only stops them from inadvertently mutating the cache
            forM_ [from, to] $ \FilePath
    x -> do
                perm <- FilePath -> IO Permissions
    getPermissions FilePath
    x
                setPermissions x perm{writable=False}