3 module Distribution
.Utils
.TempTestDir
5 , removeDirectoryRecursiveHack
8 import Distribution
.Compat
.Internal
.TempFile
(createTempDirectory
)
9 import Distribution
.Simple
.Utils
(warn
)
10 import Distribution
.Verbosity
12 import Control
.Concurrent
(threadDelay
)
13 import Control
.Exception
(bracket, throwIO
, try)
14 import Control
.Monad
(when)
16 import System
.Directory
17 import System
.IO.Error
18 #if !(MIN_VERSION_directory
(1,2,7))
19 import System
.FilePath ((</>))
21 import qualified System
.Info
(os
)
23 -- | Much like 'withTemporaryDirectory' but with a number of hacks to make
24 -- sure on windows that we can clean up the directory at the end.
25 withTestDir
:: Verbosity
-> String -> (FilePath -> IO a
) -> IO a
26 withTestDir verbosity template action
= do
27 systmpdir
<- getTemporaryDirectory
29 (createTempDirectory systmpdir template
)
30 (removeDirectoryRecursiveHack verbosity
)
33 -- | On Windows, file locks held by programs we run (in this case VCSs)
34 -- are not always released prior to completing process termination!
35 -- <https://msdn.microsoft.com/en-us/library/windows/desktop/aa365202.aspx>
36 -- This means we run into stale locks when trying to delete the test
37 -- directory. There is no sane way to wait on those locks being released,
38 -- we just have to wait, try again and hope.
40 -- In addition, on Windows a file that is not writable also cannot be deleted,
41 -- so we must try setting the permissions to readable before deleting files.
42 -- Some VCS tools on Windows create files with read-only attributes.
43 removeDirectoryRecursiveHack
:: Verbosity
-> FilePath -> IO ()
44 removeDirectoryRecursiveHack verbosity dir | isWindows
= go
1
46 isWindows
= System
.Info
.os
== "mingw32"
51 res
<- try $ removePathForcibly dir
54 -- wait a second and try again
55 |
isPermissionError e
&& n
< limit
-> do
59 -- but if we hit the limt warn and fail.
60 |
isPermissionError e
-> do
62 "Windows file locking hack: hit the retry limit "
64 ++ " while trying to remove "
68 -- or it's a different error fail.
69 |
otherwise -> throwIO e
73 "Windows file locking hack: had to try "
75 ++ " times to remove "
77 removeDirectoryRecursiveHack _ dir
= removeDirectoryRecursive dir
79 #if !(MIN_VERSION_directory
(1,2,7))
80 -- A simplified version that ought to work for our use case here, and does
81 -- not rely on directory internals.
82 removePathForcibly
:: FilePath -> IO ()
83 removePathForcibly path
= do
84 makeRemovable path `catchIOError`
\ _
-> pure
()
85 isDir
<- doesDirectoryExist path
88 entries
<- getDirectoryContents path
90 [ removePathForcibly
(path
</> entry
)
91 | entry
<- entries
, entry
/= ".", entry
/= ".." ]
96 makeRemovable
:: FilePath -> IO ()
98 setPermissions p emptyPermissions
{