{-# LANGUAGE CPP #-}
module GHC.Utils.TmpFs
( TmpFs
, initTmpFs
, forkTmpFsFrom
, mergeTmpFsInto
, PathsToClean(..)
, emptyPathsToClean
, TempFileLifetime(..)
, TempDir (..)
, cleanTempDirs
, cleanTempFiles
, cleanCurrentModuleTempFiles
, keepCurrentModuleTempFiles
, addFilesToClean
, changeTempFilesLifetime
, newTempName
, newTempLibName
, newTempSubDir
, withSystemTempDirectory
, withTempDirectory
)
where
import GHC.Prelude
import GHC.Utils.Error
import GHC.Utils.Outputable
import GHC.Utils.Logger
import GHC.Utils.Misc
import GHC.Utils.Exception as Exception
import GHC.Driver.Phases
import Data.List (partition)
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Map as Map
import Data.Map (Map)
import Data.IORef
import System.Directory
import System.FilePath
import System.IO.Error
#if !defined(mingw32_HOST_OS)
import qualified System.Posix.Internals
#endif
data TmpFs = TmpFs
{ TmpFs -> IORef (Map FilePath FilePath)
tmp_dirs_to_clean :: IORef (Map FilePath FilePath)
, TmpFs -> IORef Int
tmp_next_suffix :: IORef Int
, TmpFs -> IORef PathsToClean
tmp_files_to_clean :: IORef PathsToClean
, TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean :: IORef PathsToClean
}
data PathsToClean = PathsToClean
{ PathsToClean -> Set FilePath
ptcGhcSession :: !(Set FilePath)
, PathsToClean -> Set FilePath
ptcCurrentModule :: !(Set FilePath)
}
data TempFileLifetime
= TFL_CurrentModule
| TFL_GhcSession
deriving (Int -> TempFileLifetime -> ShowS
[TempFileLifetime] -> ShowS
TempFileLifetime -> FilePath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [TempFileLifetime] -> ShowS
$cshowList :: [TempFileLifetime] -> ShowS
show :: TempFileLifetime -> FilePath
$cshow :: TempFileLifetime -> FilePath
showsPrec :: Int -> TempFileLifetime -> ShowS
$cshowsPrec :: Int -> TempFileLifetime -> ShowS
Show)
newtype TempDir = TempDir FilePath
emptyPathsToClean :: PathsToClean
emptyPathsToClean :: PathsToClean
emptyPathsToClean = Set FilePath -> Set FilePath -> PathsToClean
PathsToClean forall a. Set a
Set.empty forall a. Set a
Set.empty
mergePathsToClean :: PathsToClean -> PathsToClean -> PathsToClean
mergePathsToClean :: PathsToClean -> PathsToClean -> PathsToClean
mergePathsToClean PathsToClean
x PathsToClean
y = PathsToClean
{ ptcGhcSession :: Set FilePath
ptcGhcSession = forall a. Ord a => Set a -> Set a -> Set a
Set.union (PathsToClean -> Set FilePath
ptcGhcSession PathsToClean
x) (PathsToClean -> Set FilePath
ptcGhcSession PathsToClean
y)
, ptcCurrentModule :: Set FilePath
ptcCurrentModule = forall a. Ord a => Set a -> Set a -> Set a
Set.union (PathsToClean -> Set FilePath
ptcCurrentModule PathsToClean
x) (PathsToClean -> Set FilePath
ptcCurrentModule PathsToClean
y)
}
initTmpFs :: IO TmpFs
initTmpFs :: IO TmpFs
initTmpFs = do
IORef PathsToClean
files <- forall a. a -> IO (IORef a)
newIORef PathsToClean
emptyPathsToClean
IORef PathsToClean
subdirs <- forall a. a -> IO (IORef a)
newIORef PathsToClean
emptyPathsToClean
IORef (Map FilePath FilePath)
dirs <- forall a. a -> IO (IORef a)
newIORef forall k a. Map k a
Map.empty
IORef Int
next <- forall a. a -> IO (IORef a)
newIORef Int
0
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ TmpFs
{ tmp_files_to_clean :: IORef PathsToClean
tmp_files_to_clean = IORef PathsToClean
files
, tmp_subdirs_to_clean :: IORef PathsToClean
tmp_subdirs_to_clean = IORef PathsToClean
subdirs
, tmp_dirs_to_clean :: IORef (Map FilePath FilePath)
tmp_dirs_to_clean = IORef (Map FilePath FilePath)
dirs
, tmp_next_suffix :: IORef Int
tmp_next_suffix = IORef Int
next
}
forkTmpFsFrom :: TmpFs -> IO TmpFs
forkTmpFsFrom :: TmpFs -> IO TmpFs
forkTmpFsFrom TmpFs
old = do
IORef PathsToClean
files <- forall a. a -> IO (IORef a)
newIORef PathsToClean
emptyPathsToClean
IORef PathsToClean
subdirs <- forall a. a -> IO (IORef a)
newIORef PathsToClean
emptyPathsToClean
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ TmpFs
{ tmp_files_to_clean :: IORef PathsToClean
tmp_files_to_clean = IORef PathsToClean
files
, tmp_subdirs_to_clean :: IORef PathsToClean
tmp_subdirs_to_clean = IORef PathsToClean
subdirs
, tmp_dirs_to_clean :: IORef (Map FilePath FilePath)
tmp_dirs_to_clean = TmpFs -> IORef (Map FilePath FilePath)
tmp_dirs_to_clean TmpFs
old
, tmp_next_suffix :: IORef Int
tmp_next_suffix = TmpFs -> IORef Int
tmp_next_suffix TmpFs
old
}
mergeTmpFsInto :: TmpFs -> TmpFs -> IO ()
mergeTmpFsInto :: TmpFs -> TmpFs -> IO ()
mergeTmpFsInto TmpFs
src TmpFs
dst = do
PathsToClean
src_files <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
src) (\PathsToClean
s -> (PathsToClean
emptyPathsToClean, PathsToClean
s))
PathsToClean
src_subdirs <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
src) (\PathsToClean
s -> (PathsToClean
emptyPathsToClean, PathsToClean
s))
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
dst) (\PathsToClean
s -> (PathsToClean -> PathsToClean -> PathsToClean
mergePathsToClean PathsToClean
src_files PathsToClean
s, ()))
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
dst) (\PathsToClean
s -> (PathsToClean -> PathsToClean -> PathsToClean
mergePathsToClean PathsToClean
src_subdirs PathsToClean
s, ()))
cleanTempDirs :: Logger -> TmpFs -> IO ()
cleanTempDirs :: Logger -> TmpFs -> IO ()
cleanTempDirs Logger
logger TmpFs
tmpfs
= forall a. IO a -> IO a
mask_
forall a b. (a -> b) -> a -> b
$ do let ref :: IORef (Map FilePath FilePath)
ref = TmpFs -> IORef (Map FilePath FilePath)
tmp_dirs_to_clean TmpFs
tmpfs
Map FilePath FilePath
ds <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (Map FilePath FilePath)
ref forall a b. (a -> b) -> a -> b
$ \Map FilePath FilePath
ds -> (forall k a. Map k a
Map.empty, Map FilePath FilePath
ds)
Logger -> [FilePath] -> IO ()
removeTmpDirs Logger
logger (forall k a. Map k a -> [a]
Map.elems Map FilePath FilePath
ds)
cleanTempFiles :: Logger -> TmpFs -> IO ()
cleanTempFiles :: Logger -> TmpFs -> IO ()
cleanTempFiles Logger
logger TmpFs
tmpfs
= forall a. IO a -> IO a
mask_
forall a b. (a -> b) -> a -> b
$ do forall {b}. ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith (Logger -> [FilePath] -> IO ()
removeTmpFiles Logger
logger) (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
tmpfs)
forall {b}. ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith (Logger -> [FilePath] -> IO ()
removeTmpSubdirs Logger
logger) (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
tmpfs)
where
removeWith :: ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith [FilePath] -> IO b
remove IORef PathsToClean
ref = do
[FilePath]
to_delete <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef PathsToClean
ref forall a b. (a -> b) -> a -> b
$
\PathsToClean
{ ptcCurrentModule :: PathsToClean -> Set FilePath
ptcCurrentModule = Set FilePath
cm_paths
, ptcGhcSession :: PathsToClean -> Set FilePath
ptcGhcSession = Set FilePath
gs_paths
} -> ( PathsToClean
emptyPathsToClean
, forall a. Set a -> [a]
Set.toList Set FilePath
cm_paths forall a. [a] -> [a] -> [a]
++ forall a. Set a -> [a]
Set.toList Set FilePath
gs_paths)
[FilePath] -> IO b
remove [FilePath]
to_delete
keepCurrentModuleTempFiles :: HasCallStack => Logger -> TmpFs -> IO ()
keepCurrentModuleTempFiles :: HasCallStack => Logger -> TmpFs -> IO ()
keepCurrentModuleTempFiles Logger
logger TmpFs
tmpfs
= forall a. IO a -> IO a
mask_
forall a b. (a -> b) -> a -> b
$ do [FilePath]
to_keep_files <- IORef PathsToClean -> IO [FilePath]
keep (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
tmpfs)
[FilePath]
to_keep_subdirs <- IORef PathsToClean -> IO [FilePath]
keep (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
tmpfs)
forall {k}. [FilePath] -> IORef (Map k FilePath) -> IO ()
keepDirs ([FilePath]
to_keep_files forall a. [a] -> [a] -> [a]
++ [FilePath]
to_keep_subdirs) (TmpFs -> IORef (Map FilePath FilePath)
tmp_dirs_to_clean TmpFs
tmpfs)
where
keepDirs :: [FilePath] -> IORef (Map k FilePath) -> IO ()
keepDirs [FilePath]
keeps IORef (Map k FilePath)
ref = do
let keep_dirs :: Set FilePath
keep_dirs = forall a. Ord a => [a] -> Set a
Set.fromList (forall a b. (a -> b) -> [a] -> [b]
map ShowS
takeDirectory [FilePath]
keeps)
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (Map k FilePath)
ref forall a b. (a -> b) -> a -> b
$ \Map k FilePath
m -> (forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter (\FilePath
fp -> FilePath
fp forall a. Ord a => a -> Set a -> Bool
`Set.notMember` Set FilePath
keep_dirs) Map k FilePath
m, ())
keep :: IORef PathsToClean -> IO [FilePath]
keep IORef PathsToClean
ref = do
[FilePath]
to_keep <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef PathsToClean
ref forall a b. (a -> b) -> a -> b
$
\ptc :: PathsToClean
ptc@PathsToClean{ptcCurrentModule :: PathsToClean -> Set FilePath
ptcCurrentModule = Set FilePath
cm_paths} ->
(PathsToClean
ptc {ptcCurrentModule :: Set FilePath
ptcCurrentModule = forall a. Set a
Set.empty}, forall a. Set a -> [a]
Set.toList Set FilePath
cm_paths)
Logger -> Int -> SDoc -> IO ()
debugTraceMsg Logger
logger Int
2 (forall doc. IsLine doc => FilePath -> doc
text FilePath
"Keeping:" forall doc. IsLine doc => doc -> doc -> doc
<+> forall doc. IsLine doc => [doc] -> doc
hsep (forall a b. (a -> b) -> [a] -> [b]
map forall doc. IsLine doc => FilePath -> doc
text [FilePath]
to_keep))
forall (m :: * -> *) a. Monad m => a -> m a
return [FilePath]
to_keep
cleanCurrentModuleTempFiles :: Logger -> TmpFs -> IO ()
cleanCurrentModuleTempFiles :: Logger -> TmpFs -> IO ()
cleanCurrentModuleTempFiles Logger
logger TmpFs
tmpfs
= forall a. IO a -> IO a
mask_
forall a b. (a -> b) -> a -> b
$ do forall {b}. ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith (Logger -> [FilePath] -> IO ()
removeTmpFiles Logger
logger) (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
tmpfs)
forall {b}. ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith (Logger -> [FilePath] -> IO ()
removeTmpSubdirs Logger
logger) (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
tmpfs)
where
removeWith :: ([FilePath] -> IO b) -> IORef PathsToClean -> IO b
removeWith [FilePath] -> IO b
remove IORef PathsToClean
ref = do
[FilePath]
to_delete <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef PathsToClean
ref forall a b. (a -> b) -> a -> b
$
\ptc :: PathsToClean
ptc@PathsToClean{ptcCurrentModule :: PathsToClean -> Set FilePath
ptcCurrentModule = Set FilePath
cm_paths} ->
(PathsToClean
ptc {ptcCurrentModule :: Set FilePath
ptcCurrentModule = forall a. Set a
Set.empty}, forall a. Set a -> [a]
Set.toList Set FilePath
cm_paths)
[FilePath] -> IO b
remove [FilePath]
to_delete
addFilesToClean :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addFilesToClean :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addFilesToClean TmpFs
tmpfs TempFileLifetime
lifetime [FilePath]
new_files =
IORef PathsToClean -> TempFileLifetime -> [FilePath] -> IO ()
addToClean (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
tmpfs) TempFileLifetime
lifetime [FilePath]
new_files
addSubdirsToClean :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addSubdirsToClean :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addSubdirsToClean TmpFs
tmpfs TempFileLifetime
lifetime [FilePath]
new_subdirs =
IORef PathsToClean -> TempFileLifetime -> [FilePath] -> IO ()
addToClean (TmpFs -> IORef PathsToClean
tmp_subdirs_to_clean TmpFs
tmpfs) TempFileLifetime
lifetime [FilePath]
new_subdirs
addToClean :: IORef PathsToClean -> TempFileLifetime -> [FilePath] -> IO ()
addToClean :: IORef PathsToClean -> TempFileLifetime -> [FilePath] -> IO ()
addToClean IORef PathsToClean
ref TempFileLifetime
lifetime [FilePath]
new_filepaths = forall a. IORef a -> (a -> a) -> IO ()
modifyIORef' IORef PathsToClean
ref forall a b. (a -> b) -> a -> b
$
\PathsToClean
{ ptcCurrentModule :: PathsToClean -> Set FilePath
ptcCurrentModule = Set FilePath
cm_paths
, ptcGhcSession :: PathsToClean -> Set FilePath
ptcGhcSession = Set FilePath
gs_paths
} -> case TempFileLifetime
lifetime of
TempFileLifetime
TFL_CurrentModule -> PathsToClean
{ ptcCurrentModule :: Set FilePath
ptcCurrentModule = Set FilePath
cm_paths forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set FilePath
new_filepaths_set
, ptcGhcSession :: Set FilePath
ptcGhcSession = Set FilePath
gs_paths forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Set FilePath
new_filepaths_set
}
TempFileLifetime
TFL_GhcSession -> PathsToClean
{ ptcCurrentModule :: Set FilePath
ptcCurrentModule = Set FilePath
cm_paths forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Set FilePath
new_filepaths_set
, ptcGhcSession :: Set FilePath
ptcGhcSession = Set FilePath
gs_paths forall a. Ord a => Set a -> Set a -> Set a
`Set.union` Set FilePath
new_filepaths_set
}
where
new_filepaths_set :: Set FilePath
new_filepaths_set = forall a. Ord a => [a] -> Set a
Set.fromList [FilePath]
new_filepaths
changeTempFilesLifetime :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
changeTempFilesLifetime :: TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
changeTempFilesLifetime TmpFs
tmpfs TempFileLifetime
lifetime [FilePath]
files = do
PathsToClean
{ ptcCurrentModule :: PathsToClean -> Set FilePath
ptcCurrentModule = Set FilePath
cm_paths
, ptcGhcSession :: PathsToClean -> Set FilePath
ptcGhcSession = Set FilePath
gs_paths
} <- forall a. IORef a -> IO a
readIORef (TmpFs -> IORef PathsToClean
tmp_files_to_clean TmpFs
tmpfs)
let old_set :: Set FilePath
old_set = case TempFileLifetime
lifetime of
TempFileLifetime
TFL_CurrentModule -> Set FilePath
gs_paths
TempFileLifetime
TFL_GhcSession -> Set FilePath
cm_paths
existing_files :: [FilePath]
existing_files = [FilePath
f | FilePath
f <- [FilePath]
files, FilePath
f forall a. Ord a => a -> Set a -> Bool
`Set.member` Set FilePath
old_set]
TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addFilesToClean TmpFs
tmpfs TempFileLifetime
lifetime [FilePath]
existing_files
newTempSuffix :: TmpFs -> IO Int
newTempSuffix :: TmpFs -> IO Int
newTempSuffix TmpFs
tmpfs =
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' (TmpFs -> IORef Int
tmp_next_suffix TmpFs
tmpfs) forall a b. (a -> b) -> a -> b
$ \Int
n -> (Int
nforall a. Num a => a -> a -> a
+Int
1,Int
n)
newTempName :: Logger -> TmpFs -> TempDir -> TempFileLifetime -> Suffix -> IO FilePath
newTempName :: Logger
-> TmpFs -> TempDir -> TempFileLifetime -> FilePath -> IO FilePath
newTempName Logger
logger TmpFs
tmpfs TempDir
tmp_dir TempFileLifetime
lifetime FilePath
extn
= do FilePath
d <- Logger -> TmpFs -> TempDir -> IO FilePath
getTempDir Logger
logger TmpFs
tmpfs TempDir
tmp_dir
FilePath -> IO FilePath
findTempName (FilePath
d FilePath -> ShowS
</> FilePath
"ghc_")
where
findTempName :: FilePath -> IO FilePath
findTempName :: FilePath -> IO FilePath
findTempName FilePath
prefix
= do Int
n <- TmpFs -> IO Int
newTempSuffix TmpFs
tmpfs
let filename :: FilePath
filename = FilePath
prefix forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
n FilePath -> ShowS
<.> FilePath
extn
Bool
b <- FilePath -> IO Bool
doesFileExist FilePath
filename
if Bool
b then FilePath -> IO FilePath
findTempName FilePath
prefix
else do
TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addFilesToClean TmpFs
tmpfs TempFileLifetime
lifetime [FilePath
filename]
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
filename
newTempSubDir :: Logger -> TmpFs -> TempDir -> IO FilePath
newTempSubDir :: Logger -> TmpFs -> TempDir -> IO FilePath
newTempSubDir Logger
logger TmpFs
tmpfs TempDir
tmp_dir
= do FilePath
d <- Logger -> TmpFs -> TempDir -> IO FilePath
getTempDir Logger
logger TmpFs
tmpfs TempDir
tmp_dir
FilePath -> IO FilePath
findTempDir (FilePath
d FilePath -> ShowS
</> FilePath
"ghc_")
where
findTempDir :: FilePath -> IO FilePath
findTempDir :: FilePath -> IO FilePath
findTempDir FilePath
prefix
= do Int
n <- TmpFs -> IO Int
newTempSuffix TmpFs
tmpfs
let name :: FilePath
name = FilePath
prefix forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
n
Bool
b <- FilePath -> IO Bool
doesDirectoryExist FilePath
name
if Bool
b then FilePath -> IO FilePath
findTempDir FilePath
prefix
else (do
FilePath -> IO ()
createDirectory FilePath
name
TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addSubdirsToClean TmpFs
tmpfs TempFileLifetime
TFL_GhcSession [FilePath
name]
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
name)
forall a. IO a -> (IOException -> IO a) -> IO a
`Exception.catchIO` \IOException
e -> if IOException -> Bool
isAlreadyExistsError IOException
e
then FilePath -> IO FilePath
findTempDir FilePath
prefix else forall a. IOException -> IO a
ioError IOException
e
newTempLibName :: Logger -> TmpFs -> TempDir -> TempFileLifetime -> Suffix
-> IO (FilePath, FilePath, String)
newTempLibName :: Logger
-> TmpFs
-> TempDir
-> TempFileLifetime
-> FilePath
-> IO (FilePath, FilePath, FilePath)
newTempLibName Logger
logger TmpFs
tmpfs TempDir
tmp_dir TempFileLifetime
lifetime FilePath
extn
= do FilePath
d <- Logger -> TmpFs -> TempDir -> IO FilePath
getTempDir Logger
logger TmpFs
tmpfs TempDir
tmp_dir
FilePath -> FilePath -> IO (FilePath, FilePath, FilePath)
findTempName FilePath
d (FilePath
"ghc_")
where
findTempName :: FilePath -> String -> IO (FilePath, FilePath, String)
findTempName :: FilePath -> FilePath -> IO (FilePath, FilePath, FilePath)
findTempName FilePath
dir FilePath
prefix
= do Int
n <- TmpFs -> IO Int
newTempSuffix TmpFs
tmpfs
let libname :: FilePath
libname = FilePath
prefix forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
n
filename :: FilePath
filename = FilePath
dir FilePath -> ShowS
</> FilePath
"lib" forall a. [a] -> [a] -> [a]
++ FilePath
libname FilePath -> ShowS
<.> FilePath
extn
Bool
b <- FilePath -> IO Bool
doesFileExist FilePath
filename
if Bool
b then FilePath -> FilePath -> IO (FilePath, FilePath, FilePath)
findTempName FilePath
dir FilePath
prefix
else do
TmpFs -> TempFileLifetime -> [FilePath] -> IO ()
addFilesToClean TmpFs
tmpfs TempFileLifetime
lifetime [FilePath
filename]
forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath
filename, FilePath
dir, FilePath
libname)
getTempDir :: Logger -> TmpFs -> TempDir -> IO FilePath
getTempDir :: Logger -> TmpFs -> TempDir -> IO FilePath
getTempDir Logger
logger TmpFs
tmpfs (TempDir FilePath
tmp_dir) = do
Map FilePath FilePath
mapping <- forall a. IORef a -> IO a
readIORef IORef (Map FilePath FilePath)
dir_ref
case forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
tmp_dir Map FilePath FilePath
mapping of
Maybe FilePath
Nothing -> do
Int
pid <- IO Int
getProcessID
let prefix :: FilePath
prefix = FilePath
tmp_dir FilePath -> ShowS
</> FilePath
"ghc" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
pid forall a. [a] -> [a] -> [a]
++ FilePath
"_"
forall a. IO a -> IO a
mask_ forall a b. (a -> b) -> a -> b
$ FilePath -> IO FilePath
mkTempDir FilePath
prefix
Just FilePath
dir -> forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
dir
where
dir_ref :: IORef (Map FilePath FilePath)
dir_ref = TmpFs -> IORef (Map FilePath FilePath)
tmp_dirs_to_clean TmpFs
tmpfs
mkTempDir :: FilePath -> IO FilePath
mkTempDir :: FilePath -> IO FilePath
mkTempDir FilePath
prefix = do
Int
n <- TmpFs -> IO Int
newTempSuffix TmpFs
tmpfs
let our_dir :: FilePath
our_dir = FilePath
prefix forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
n
FilePath -> IO ()
createDirectory FilePath
our_dir
Maybe FilePath
their_dir <- forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef (Map FilePath FilePath)
dir_ref forall a b. (a -> b) -> a -> b
$ \Map FilePath FilePath
mapping ->
case forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
tmp_dir Map FilePath FilePath
mapping of
Just FilePath
dir -> (Map FilePath FilePath
mapping, forall a. a -> Maybe a
Just FilePath
dir)
Maybe FilePath
Nothing -> (forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert FilePath
tmp_dir FilePath
our_dir Map FilePath FilePath
mapping, forall a. Maybe a
Nothing)
case Maybe FilePath
their_dir of
Maybe FilePath
Nothing -> do
Logger -> Int -> SDoc -> IO ()
debugTraceMsg Logger
logger Int
2 forall a b. (a -> b) -> a -> b
$
forall doc. IsLine doc => FilePath -> doc
text FilePath
"Created temporary directory:" forall doc. IsLine doc => doc -> doc -> doc
<+> forall doc. IsLine doc => FilePath -> doc
text FilePath
our_dir
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
our_dir
Just FilePath
dir -> do
FilePath -> IO ()
removeDirectory FilePath
our_dir
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
dir
forall a. IO a -> (IOException -> IO a) -> IO a
`Exception.catchIO` \IOException
e -> if IOException -> Bool
isAlreadyExistsError IOException
e
then FilePath -> IO FilePath
mkTempDir FilePath
prefix else forall a. IOException -> IO a
ioError IOException
e
removeTmpDirs :: Logger -> [FilePath] -> IO ()
removeTmpDirs :: Logger -> [FilePath] -> IO ()
removeTmpDirs Logger
logger [FilePath]
ds
= forall a. Logger -> FilePath -> FilePath -> IO a -> IO a
traceCmd Logger
logger FilePath
"Deleting temp dirs"
(FilePath
"Deleting: " forall a. [a] -> [a] -> [a]
++ [FilePath] -> FilePath
unwords [FilePath]
ds)
(forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Logger -> (FilePath -> IO ()) -> FilePath -> IO ()
removeWith Logger
logger FilePath -> IO ()
removeDirectory) [FilePath]
ds)
removeTmpFiles :: Logger -> [FilePath] -> IO ()
removeTmpFiles :: Logger -> [FilePath] -> IO ()
removeTmpFiles Logger
logger [FilePath]
fs
= IO () -> IO ()
warnNon forall a b. (a -> b) -> a -> b
$
forall a. Logger -> FilePath -> FilePath -> IO a -> IO a
traceCmd Logger
logger FilePath
"Deleting temp files"
(FilePath
"Deleting: " forall a. [a] -> [a] -> [a]
++ [FilePath] -> FilePath
unwords [FilePath]
deletees)
(forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Logger -> (FilePath -> IO ()) -> FilePath -> IO ()
removeWith Logger
logger FilePath -> IO ()
removeFile) [FilePath]
deletees)
where
warnNon :: IO () -> IO ()
warnNon IO ()
act
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
non_deletees = IO ()
act
| Bool
otherwise = do
Logger -> SDoc -> IO ()
putMsg Logger
logger (forall doc. IsLine doc => FilePath -> doc
text FilePath
"WARNING - NOT deleting source files:"
forall doc. IsLine doc => doc -> doc -> doc
<+> forall doc. IsLine doc => [doc] -> doc
hsep (forall a b. (a -> b) -> [a] -> [b]
map forall doc. IsLine doc => FilePath -> doc
text [FilePath]
non_deletees))
IO ()
act
([FilePath]
non_deletees, [FilePath]
deletees) = forall a. (a -> Bool) -> [a] -> ([a], [a])
partition FilePath -> Bool
isHaskellUserSrcFilename [FilePath]
fs
removeTmpSubdirs :: Logger -> [FilePath] -> IO ()
removeTmpSubdirs :: Logger -> [FilePath] -> IO ()
removeTmpSubdirs Logger
logger [FilePath]
fs
= forall a. Logger -> FilePath -> FilePath -> IO a -> IO a
traceCmd Logger
logger FilePath
"Deleting temp subdirs"
(FilePath
"Deleting: " forall a. [a] -> [a] -> [a]
++ [FilePath] -> FilePath
unwords [FilePath]
fs)
(forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Logger -> (FilePath -> IO ()) -> FilePath -> IO ()
removeWith Logger
logger FilePath -> IO ()
removeDirectory) [FilePath]
fs)
removeWith :: Logger -> (FilePath -> IO ()) -> FilePath -> IO ()
removeWith :: Logger -> (FilePath -> IO ()) -> FilePath -> IO ()
removeWith Logger
logger FilePath -> IO ()
remover FilePath
f = FilePath -> IO ()
remover FilePath
f forall a. IO a -> (IOException -> IO a) -> IO a
`Exception.catchIO`
(\IOException
e ->
let msg :: SDoc
msg = if IOException -> Bool
isDoesNotExistError IOException
e
then forall doc. IsLine doc => FilePath -> doc
text FilePath
"Warning: deleting non-existent" forall doc. IsLine doc => doc -> doc -> doc
<+> forall doc. IsLine doc => FilePath -> doc
text FilePath
f
else forall doc. IsLine doc => FilePath -> doc
text FilePath
"Warning: exception raised when deleting"
forall doc. IsLine doc => doc -> doc -> doc
<+> forall doc. IsLine doc => FilePath -> doc
text FilePath
f forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => doc
colon
forall doc. IsDoc doc => doc -> doc -> doc
$$ forall doc. IsLine doc => FilePath -> doc
text (forall a. Show a => a -> FilePath
show IOException
e)
in Logger -> Int -> SDoc -> IO ()
debugTraceMsg Logger
logger Int
2 SDoc
msg
)
#if defined(mingw32_HOST_OS)
foreign import ccall unsafe "_getpid" getProcessID :: IO Int
#else
getProcessID :: IO Int
getProcessID :: IO Int
getProcessID = IO CPid
System.Posix.Internals.c_getpid forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (Integral a, Num b) => a -> b
fromIntegral
#endif
withSystemTempDirectory :: String
-> (FilePath -> IO a)
-> IO a
withSystemTempDirectory :: forall a. FilePath -> (FilePath -> IO a) -> IO a
withSystemTempDirectory FilePath
template FilePath -> IO a
action =
IO FilePath
getTemporaryDirectory forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \FilePath
tmpDir -> forall a. FilePath -> FilePath -> (FilePath -> IO a) -> IO a
withTempDirectory FilePath
tmpDir FilePath
template FilePath -> IO a
action
withTempDirectory :: FilePath
-> String
-> (FilePath -> IO a)
-> IO a
withTempDirectory :: forall a. FilePath -> FilePath -> (FilePath -> IO a) -> IO a
withTempDirectory FilePath
targetDir FilePath
template =
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Exception.bracket
(FilePath -> FilePath -> IO FilePath
createTempDirectory FilePath
targetDir FilePath
template)
(IO () -> IO ()
ignoringIOErrors forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> IO ()
removeDirectoryRecursive)
ignoringIOErrors :: IO () -> IO ()
ignoringIOErrors :: IO () -> IO ()
ignoringIOErrors IO ()
ioe = IO ()
ioe forall a. IO a -> (IOException -> IO a) -> IO a
`Exception.catchIO` forall a b. a -> b -> a
const (forall (m :: * -> *) a. Monad m => a -> m a
return ())
createTempDirectory :: FilePath -> String -> IO FilePath
createTempDirectory :: FilePath -> FilePath -> IO FilePath
createTempDirectory FilePath
dir FilePath
template = do
Int
pid <- IO Int
getProcessID
Int -> IO FilePath
findTempName Int
pid
where findTempName :: Int -> IO FilePath
findTempName Int
x = do
let path :: FilePath
path = FilePath
dir FilePath -> ShowS
</> FilePath
template forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show Int
x
FilePath -> IO ()
createDirectory FilePath
path
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
path
forall a. IO a -> (IOException -> IO a) -> IO a
`Exception.catchIO` \IOException
e -> if IOException -> Bool
isAlreadyExistsError IOException
e
then Int -> IO FilePath
findTempName (Int
xforall a. Num a => a -> a -> a
+Int
1) else forall a. IOException -> IO a
ioError IOException
e