@@ -97,6 +97,9 @@ module Distribution.Simple.Utils
9797 , copyFileTo
9898 , copyFileToCwd
9999
100+ -- * removing files
101+ , removeFileForcibly
102+
100103 -- * installing files
101104 , installOrdinaryFile
102105 , installExecutableFile
@@ -244,6 +247,7 @@ import Data.Typeable
244247 ( cast
245248 )
246249
250+ import Control.Concurrent (threadDelay )
247251import qualified Control.Exception as Exception
248252import Data.Time.Clock.POSIX (POSIXTime , getPOSIXTime )
249253import qualified Data.Version as DV
@@ -1812,6 +1816,26 @@ copyFilesWith doCopy verbosity targetDir srcFiles = withFrozenCallStack $ do
18121816copyFiles :: Verbosity -> FilePath -> [(FilePath , FilePath )] -> IO ()
18131817copyFiles v fp fs = withFrozenCallStack (copyFilesWith copyFileVerbose v fp fs)
18141818
1819+ -- | A robust helper to remove an existing file, which does not throw
1820+ -- an exception if such file never existed, thus akin to removePathForcibly.
1821+ removeFileForcibly :: FilePath -> IO ()
1822+ removeFileForcibly fp = catch (removeFile fp) $ \ case
1823+ e
1824+ -- If the file never existed in the first place, we are golden.
1825+ | isDoesNotExistError e -> pure ()
1826+ -- If we got a permission error, chances are that it's a read-only
1827+ -- file on Windows. Removing read-only attribute ourselves requires
1828+ -- reaching out for internal API, so instead of it we call 'removePathForcibly',
1829+ -- which is a bit of overkill for a single file, but well.
1830+ | isPermissionError e -> removePathForcibly fp
1831+ -- If device is busy, wait 1ms and give it another go.
1832+ -- EBUSY from unlink(2) gets mapped to UnsatisfiedConstraints.
1833+ | ioeGetErrorType e == GHC. UnsatisfiedConstraints -> do
1834+ threadDelay 1000
1835+ removeFile fp
1836+ -- Else we give up.
1837+ | otherwise -> throwIO e
1838+
18151839-- | This is like 'copyFiles' but uses 'installOrdinaryFile'.
18161840installOrdinaryFiles :: Verbosity -> FilePath -> [(FilePath , FilePath )] -> IO ()
18171841installOrdinaryFiles v fp fs = withFrozenCallStack (copyFilesWith installOrdinaryFile v fp fs)
0 commit comments