Use strongly types GHCupPath
and restrict destructive operations
This commit is contained in:
parent
fa924eac15
commit
c9790e5823
@ -44,7 +44,6 @@ import Data.Vector ( Vector
|
||||
import Data.Versions hiding ( str )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Directory ( canonicalizePath )
|
||||
import System.FilePath
|
||||
import System.Exit
|
||||
import System.IO.Unsafe
|
||||
|
@ -52,7 +52,6 @@ import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import Safe
|
||||
import System.Directory
|
||||
import System.Process ( readProcess )
|
||||
import System.FilePath
|
||||
import Text.HTML.TagSoup hiding ( Tag )
|
||||
|
@ -494,7 +494,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger $ logError $ T.pack $ prettyShow err
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory "
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory "
|
||||
<> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 9
|
||||
@ -553,7 +553,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger $ logError $ T.pack $ prettyShow err
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory "
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory "
|
||||
<> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 9
|
||||
|
@ -18,6 +18,7 @@ import GHCup.OptParse.Common
|
||||
import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
@ -446,21 +447,21 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
case keepDirs settings of
|
||||
Never -> runLogger (logError $ T.pack $ prettyShow err)
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 3
|
||||
VLeft err@(V (BuildFailed tmpdir _, ())) -> do
|
||||
case keepDirs settings of
|
||||
Never -> runLogger (logError $ T.pack $ prettyShow err)
|
||||
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
|
||||
"Check the logs at " <> T.pack logsDir <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
|
||||
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
|
||||
pure $ ExitFailure 3
|
||||
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 3
|
||||
|
||||
|
||||
@ -512,7 +513,7 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
installHLS :: InstallOptions -> IO ExitCode
|
||||
@ -572,7 +573,7 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
installStack :: InstallOptions -> IO ExitCode
|
||||
@ -623,6 +624,6 @@ install installCommand settings getAppState' runLogger = case installCommand of
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
logError $ T.pack $ prettyShow e
|
||||
logError $ "Also check the logs in " <> T.pack logsDir
|
||||
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir)
|
||||
pure $ ExitFailure 4
|
||||
|
||||
|
@ -32,7 +32,6 @@ import Data.List ( intercalate )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Options.Applicative hiding ( style )
|
||||
import Prelude hiding ( appendFile )
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
|
@ -17,6 +17,7 @@ import GHCup
|
||||
import GHCup.Errors
|
||||
import GHCup.OptParse.Common
|
||||
import GHCup.Types
|
||||
import GHCup.Utils
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
@ -299,7 +300,7 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
pure $ ExitFailure 30
|
||||
|
||||
(WhereisBaseDir, _) -> do
|
||||
liftIO $ putStr baseDir
|
||||
liftIO $ putStr $ fromGHCupPath baseDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisBinDir, _) -> do
|
||||
@ -307,13 +308,13 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisCacheDir, _) -> do
|
||||
liftIO $ putStr cacheDir
|
||||
liftIO $ putStr $ fromGHCupPath cacheDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisLogsDir, _) -> do
|
||||
liftIO $ putStr logsDir
|
||||
liftIO $ putStr $ fromGHCupPath logsDir
|
||||
pure ExitSuccess
|
||||
|
||||
(WhereisConfDir, _) -> do
|
||||
liftIO $ putStr confDir
|
||||
liftIO $ putStr $ fromGHCupPath confDir
|
||||
pure ExitSuccess
|
||||
|
@ -220,7 +220,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
let s' = AppState settings dirs keybindings ghcupInfo pfreq loggerConfig
|
||||
|
||||
race_ (liftIO $ runReaderT cleanupTrash s')
|
||||
(threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack recycleDir <> " manually"))
|
||||
(threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack (fromGHCupPath recycleDir) <> " manually"))
|
||||
|
||||
case optCommand of
|
||||
Nuke -> pure ()
|
||||
|
192
lib/GHCup.hs
192
lib/GHCup.hs
@ -77,11 +77,9 @@ import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import Safe hiding ( at )
|
||||
import System.Directory hiding ( findFiles, copyFile )
|
||||
import System.Environment
|
||||
import System.FilePath
|
||||
import System.IO.Error
|
||||
import System.IO.Temp
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import Text.Regex.Posix
|
||||
import URI.ByteString
|
||||
@ -293,8 +291,8 @@ installPackedGHC dl msubdir inst ver forceInstall = do
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- maybe (pure tmpUnpack)
|
||||
@ -319,7 +317,7 @@ installUnpackedGHC :: ( MonadReader env m
|
||||
, MonadResource m
|
||||
, MonadFail m
|
||||
)
|
||||
=> FilePath -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
||||
=> GHCupPath -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
||||
-> InstallDirResolved -- ^ Path to install to
|
||||
-> Version -- ^ The GHC version
|
||||
-> Bool -- ^ Force install
|
||||
@ -351,13 +349,13 @@ installUnpackedGHC path inst ver forceInstall
|
||||
("./configure" : ("--prefix=" <> fromInstallDir inst)
|
||||
: alpineArgs
|
||||
)
|
||||
(Just path)
|
||||
(Just $ fromGHCupPath path)
|
||||
"ghc-configure"
|
||||
Nothing
|
||||
tmpInstallDest <- lift withGHCupTmpDir
|
||||
lEM $ make ["DESTDIR=" <> tmpInstallDest, "install"] (Just path)
|
||||
lift $ logInfo $ "Merging file tree from \"" <> T.pack tmpInstallDest <> "\" to \"" <> T.pack (fromInstallDir inst) <> "\""
|
||||
fs <- mergeFileTreeAll (tmpInstallDest </> dropDrive (fromInstallDir inst))
|
||||
lEM $ make ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "install"] (Just $ fromGHCupPath path)
|
||||
lift $ logInfo $ "Merging file tree from \"" <> T.pack (fromGHCupPath tmpInstallDest) <> "\" to \"" <> T.pack (fromInstallDir inst) <> "\""
|
||||
fs <- mergeFileTreeAll (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir inst))
|
||||
(fromInstallDir inst)
|
||||
(\f t -> liftIO (install f t (not forceInstall)))
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromInstallDir inst)
|
||||
@ -472,11 +470,11 @@ installCabalBindist dlinfo ver installDir forceInstall = do
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
|
||||
case installDir of
|
||||
IsolateDir isoDir -> do -- isolated install
|
||||
@ -484,7 +482,7 @@ installCabalBindist dlinfo ver installDir forceInstall = do
|
||||
liftE $ installCabalUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall
|
||||
|
||||
GHCupInternal -> do -- regular install
|
||||
liftE $ installCabalUnpacked workdir (GHCupDir binDir) ver forceInstall
|
||||
liftE $ installCabalUnpacked workdir (GHCupBinDir binDir) ver forceInstall
|
||||
|
||||
|
||||
-- | Install an unpacked cabal distribution.Symbol
|
||||
@ -501,7 +499,7 @@ installCabalUnpacked path inst ver forceInstall = do
|
||||
let destFileName = cabalFile
|
||||
<> (case inst of
|
||||
IsolateDirResolved _ -> ""
|
||||
GHCupDir _ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
_ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
let destPath = fromInstallDir inst </> destFileName
|
||||
@ -614,11 +612,11 @@ installHLSBindist dlinfo ver installDir forceInstall = do
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
workdir <- fromGHCupPath <$> maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
legacy <- liftIO $ isLegacyHLSBindist workdir
|
||||
|
||||
if
|
||||
@ -636,7 +634,7 @@ installHLSBindist dlinfo ver installDir forceInstall = do
|
||||
|
||||
GHCupInternal -> do
|
||||
if legacy
|
||||
then liftE $ installHLSUnpackedLegacy workdir (GHCupDir binDir) ver forceInstall
|
||||
then liftE $ installHLSUnpackedLegacy workdir (GHCupBinDir binDir) ver forceInstall
|
||||
else do
|
||||
inst <- ghcupHLSDir ver
|
||||
liftE $ runBuildAction tmpUnpack
|
||||
@ -671,8 +669,8 @@ installHLSUnpacked path inst ver forceInstall = do
|
||||
PlatformRequest { .. } <- lift getPlatformReq
|
||||
lift $ logInfo "Installing HLS"
|
||||
tmpInstallDest <- lift withGHCupTmpDir
|
||||
lEM $ make ["DESTDIR=" <> tmpInstallDest, "PREFIX=" <> fromInstallDir inst, "install"] (Just path)
|
||||
fs <- mergeFileTreeAll (tmpInstallDest </> dropDrive (fromInstallDir inst))
|
||||
lEM $ make ["DESTDIR=" <> fromGHCupPath tmpInstallDest, "PREFIX=" <> fromInstallDir inst, "install"] (Just path)
|
||||
fs <- mergeFileTreeAll (tmpInstallDest `appendGHCupPath` dropDrive (fromInstallDir inst))
|
||||
(fromInstallDir inst)
|
||||
(\f t -> liftIO (install f t (not forceInstall)))
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromInstallDir inst)
|
||||
@ -702,7 +700,7 @@ installHLSUnpackedLegacy path installDir ver forceInstall = do
|
||||
let toF = dropSuffix exeExt f
|
||||
<> (case installDir of
|
||||
IsolateDirResolved _ -> ""
|
||||
GHCupDir _ -> ("~" <>) . T.unpack . prettyVer $ ver
|
||||
_ -> ("~" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
|
||||
@ -720,7 +718,7 @@ installHLSUnpackedLegacy path installDir ver forceInstall = do
|
||||
toF = wrapper
|
||||
<> (case installDir of
|
||||
IsolateDirResolved _ -> ""
|
||||
GHCupDir _ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
_ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
srcWrapperPath = path </> wrapper <> exeExt
|
||||
@ -827,8 +825,8 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
workdir <- maybe (pure tmpUnpack)
|
||||
(liftE . intoSubdir tmpUnpack)
|
||||
@ -839,7 +837,7 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
-- clone from git
|
||||
Right GitBranch{..} -> do
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
let git args = execLogged "git" ("--no-pager":args) (Just tmpUnpack) "git" Nothing
|
||||
let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing
|
||||
tver <- reThrowAll @_ @'[ProcessError] DownloadFailed $ do
|
||||
let rep = fromMaybe "https://github.com/haskell/haskell-language-server.git" repo
|
||||
lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"
|
||||
@ -859,7 +857,7 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
lEM $ git fetch_args
|
||||
|
||||
lEM $ git [ "checkout", "FETCH_HEAD" ]
|
||||
(Just gpd) <- parseGenericPackageDescriptionMaybe <$> liftIO (B.readFile (tmpUnpack </> "haskell-language-server.cabal"))
|
||||
(Just gpd) <- parseGenericPackageDescriptionMaybe <$> liftIO (B.readFile (fromGHCupPath tmpUnpack </> "haskell-language-server.cabal"))
|
||||
pure . (\c -> Version Nothing c [] Nothing)
|
||||
. NE.fromList . fmap (NE.fromList . (:[]) . digits . fromIntegral)
|
||||
. versionNumbers
|
||||
@ -868,7 +866,7 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
. packageDescription
|
||||
$ gpd
|
||||
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
lift $ logInfo $ "Git version " <> T.pack ref <> " corresponds to HLS version " <> prettyVer tver
|
||||
|
||||
pure (tmpUnpack, tver)
|
||||
@ -879,30 +877,30 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
|
||||
liftE $ runBuildAction
|
||||
workdir
|
||||
(reThrowAll @_ @'[GPGError, DownloadFailed, DigestError, PatchFailed, ProcessError, FileAlreadyExistsError, CopyError] @'[BuildFailed] (BuildFailed workdir) $ do
|
||||
let tmpInstallDir = workdir </> "out"
|
||||
(reThrowAll @_ @'[GPGError, DownloadFailed, DigestError, PatchFailed, ProcessError, FileAlreadyExistsError, CopyError] @'[BuildFailed] (BuildFailed $ fromGHCupPath workdir) $ do
|
||||
let tmpInstallDir = fromGHCupPath workdir </> "out"
|
||||
liftIO $ createDirRecursive' tmpInstallDir
|
||||
|
||||
-- apply patches
|
||||
liftE $ applyAnyPatch patches workdir
|
||||
liftE $ applyAnyPatch patches (fromGHCupPath workdir)
|
||||
|
||||
-- set up project files
|
||||
cp <- case cabalProject of
|
||||
Just (Left cp)
|
||||
| isAbsolute cp -> do
|
||||
copyFileE cp (workdir </> "cabal.project") False
|
||||
copyFileE cp (fromGHCupPath workdir </> "cabal.project") False
|
||||
pure "cabal.project"
|
||||
| otherwise -> pure (takeFileName cp)
|
||||
Just (Right uri) -> do
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
cp <- liftE $ download uri Nothing Nothing tmpUnpack (Just "cabal.project") False
|
||||
copyFileE cp (workdir </> "cabal.project") False
|
||||
cp <- liftE $ download uri Nothing Nothing (fromGHCupPath tmpUnpack) (Just "cabal.project") False
|
||||
copyFileE cp (fromGHCupPath workdir </> "cabal.project") False
|
||||
pure "cabal.project"
|
||||
Nothing -> pure "cabal.project"
|
||||
forM_ cabalProjectLocal $ \uri -> do
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
cpl <- liftE $ download uri Nothing Nothing tmpUnpack (Just (cp <.> "local")) False
|
||||
copyFileE cpl (workdir </> cp <.> "local") False
|
||||
cpl <- liftE $ download uri Nothing Nothing (fromGHCupPath tmpUnpack) (Just (cp <.> "local")) False
|
||||
copyFileE cpl (fromGHCupPath workdir </> cp <.> "local") False
|
||||
artifacts <- forM (sort ghcs) $ \ghc -> do
|
||||
let ghcInstallDir = tmpInstallDir </> T.unpack (prettyVer ghc)
|
||||
liftIO $ createDirRecursive' tmpInstallDir
|
||||
@ -923,7 +921,9 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
"exe:haskell-language-server"
|
||||
, "exe:haskell-language-server-wrapper"]
|
||||
)
|
||||
(Just workdir) "cabal" Nothing
|
||||
(Just $ fromGHCupPath workdir)
|
||||
"cabal"
|
||||
Nothing
|
||||
pure ghcInstallDir
|
||||
|
||||
forM_ artifacts $ \artifact -> do
|
||||
@ -931,14 +931,14 @@ compileHLS targetHLS ghcs jobs ov installDir cabalProject cabalProjectLocal patc
|
||||
(tmpInstallDir </> "haskell-language-server-" <> takeFileName artifact <.> exeExt)
|
||||
liftIO $ renameFile (artifact </> "haskell-language-server-wrapper" <.> exeExt)
|
||||
(tmpInstallDir </> "haskell-language-server-wrapper" <.> exeExt)
|
||||
liftIO $ rmPathForcibly artifact
|
||||
liftIO $ hideError NoSuchThing $ rmFile artifact
|
||||
|
||||
case installDir of
|
||||
IsolateDir isoDir -> do
|
||||
lift $ logInfo $ "isolated installing HLS to " <> T.pack isoDir
|
||||
liftE $ installHLSUnpackedLegacy tmpInstallDir (IsolateDirResolved isoDir) installVer True
|
||||
GHCupInternal -> do
|
||||
liftE $ installHLSUnpackedLegacy tmpInstallDir (GHCupDir binDir) installVer True
|
||||
liftE $ installHLSUnpackedLegacy tmpInstallDir (GHCupBinDir binDir) installVer True
|
||||
)
|
||||
|
||||
pure installVer
|
||||
@ -1044,8 +1044,8 @@ installStackBindist dlinfo ver installDir forceInstall = do
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
|
||||
-- the subdir of the archive where we do the work
|
||||
workdir <- maybe (pure tmpUnpack) (liftE . intoSubdir tmpUnpack) (view dlSubdir dlinfo)
|
||||
@ -1055,12 +1055,12 @@ installStackBindist dlinfo ver installDir forceInstall = do
|
||||
lift $ logInfo $ "isolated installing Stack to " <> T.pack isoDir
|
||||
liftE $ installStackUnpacked workdir (IsolateDirResolved isoDir) ver forceInstall
|
||||
GHCupInternal -> do -- regular install
|
||||
liftE $ installStackUnpacked workdir (GHCupDir binDir) ver forceInstall
|
||||
liftE $ installStackUnpacked workdir (GHCupBinDir binDir) ver forceInstall
|
||||
|
||||
|
||||
-- | Install an unpacked stack distribution.
|
||||
installStackUnpacked :: (MonadReader env m, HasLog env, MonadCatch m, MonadIO m)
|
||||
=> FilePath -- ^ Path to the unpacked stack bindist (where the executable resides)
|
||||
=> GHCupPath -- ^ Path to the unpacked stack bindist (where the executable resides)
|
||||
-> InstallDirResolved
|
||||
-> Version
|
||||
-> Bool -- ^ Force install
|
||||
@ -1072,13 +1072,13 @@ installStackUnpacked path installDir ver forceInstall = do
|
||||
let destFileName = stackFile
|
||||
<> (case installDir of
|
||||
IsolateDirResolved _ -> ""
|
||||
GHCupDir _ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
_ -> ("-" <>) . T.unpack . prettyVer $ ver
|
||||
)
|
||||
<> exeExt
|
||||
destPath = fromInstallDir installDir </> destFileName
|
||||
|
||||
copyFileE
|
||||
(path </> stackFile <> exeExt)
|
||||
(fromGHCupPath path </> stackFile <> exeExt)
|
||||
destPath
|
||||
(not forceInstall)
|
||||
lift $ chmod_755 destPath
|
||||
@ -1160,7 +1160,7 @@ setGHC ver sghc mBinDir = do
|
||||
|
||||
when (isNothing mBinDir) $ do
|
||||
-- create symlink for share dir
|
||||
when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir ghcdir verS
|
||||
when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir (fromGHCupPath ghcdir) verS
|
||||
|
||||
when (sghc == SetGHCOnly) $ lift warnAboutHlsCompatibility
|
||||
|
||||
@ -1180,7 +1180,7 @@ setGHC ver sghc mBinDir = do
|
||||
-> m ()
|
||||
symlinkShareDir ghcdir ver' = do
|
||||
Dirs {..} <- getDirs
|
||||
let destdir = baseDir
|
||||
let destdir = fromGHCupPath baseDir
|
||||
case sghc of
|
||||
SetGHCOnly -> do
|
||||
let sharedir = "share"
|
||||
@ -1799,19 +1799,20 @@ rmGHCVer ver = do
|
||||
handle (\(_ :: ParseError) -> pure ()) $ liftE $ rmMajorGHCSymlinks ver
|
||||
-- then fix them (e.g. with an earlier version)
|
||||
|
||||
dir <- lift $ ghcupGHCDir ver
|
||||
dir' <- lift $ ghcupGHCDir ver
|
||||
let dir = fromGHCupPath dir'
|
||||
lift (getInstalledFiles GHC ver) >>= \case
|
||||
Just files -> do
|
||||
lift $ logInfo $ "Removing files safely from: " <> T.pack dir
|
||||
forM_ files (liftIO . deleteFile . (\f -> dir </> dropDrive f))
|
||||
f <- recordedInstallationFile GHC ver
|
||||
liftIO $ deleteFile f
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteFile f
|
||||
removeEmptyDirsRecursive dir
|
||||
survivors <- liftIO $ hideErrorDef [doesNotExistErrorType] [] $ listDirectory dir
|
||||
when (not (null survivors)) $ throwE $ UninstallFailed dir survivors
|
||||
Nothing -> do
|
||||
lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack dir
|
||||
lift $ recyclePathForcibly dir
|
||||
lift $ recyclePathForcibly dir'
|
||||
|
||||
v' <-
|
||||
handle
|
||||
@ -1823,7 +1824,7 @@ rmGHCVer ver = do
|
||||
|
||||
Dirs {..} <- lift getDirs
|
||||
|
||||
lift $ hideError doesNotExistErrorType $ rmDirectoryLink (baseDir </> "share")
|
||||
lift $ hideError doesNotExistErrorType $ rmDirectoryLink (fromGHCupPath baseDir </> "share")
|
||||
|
||||
|
||||
-- | Delete a cabal version. Will try to fix the @cabal@ symlink
|
||||
@ -1882,7 +1883,8 @@ rmHLSVer ver = do
|
||||
-- delete all set symlinks
|
||||
liftE rmPlainHLS
|
||||
|
||||
hlsDir <- ghcupHLSDir ver
|
||||
hlsDir' <- ghcupHLSDir ver
|
||||
let hlsDir = fromGHCupPath hlsDir'
|
||||
lift (getInstalledFiles HLS (mkTVer ver)) >>= \case
|
||||
Just files -> do
|
||||
lift $ logInfo $ "Removing files safely from: " <> T.pack hlsDir
|
||||
@ -1894,7 +1896,7 @@ rmHLSVer ver = do
|
||||
when (not (null survivors)) $ throwE $ UninstallFailed hlsDir survivors
|
||||
Nothing -> do
|
||||
lift $ logInfo $ "Removing legacy directory recursively: " <> T.pack hlsDir
|
||||
recyclePathForcibly hlsDir
|
||||
recyclePathForcibly hlsDir'
|
||||
|
||||
when (Just ver == isHlsSet) $ do
|
||||
-- set latest hls
|
||||
@ -1974,7 +1976,7 @@ rmGhcup = do
|
||||
tempFilepath <- mkGhcupTmpDir
|
||||
hideError UnsupportedOperation $
|
||||
liftIO $ hideError NoSuchThing $
|
||||
moveFile ghcupFilepath (tempFilepath </> "ghcup")
|
||||
moveFile ghcupFilepath (fromGHCupPath tempFilepath </> "ghcup")
|
||||
else
|
||||
-- delete it.
|
||||
hideError doesNotExistErrorType $ rmFile ghcupFilepath
|
||||
@ -2024,7 +2026,7 @@ rmGhcupDirs = do
|
||||
, recycleDir
|
||||
} <- getDirs
|
||||
|
||||
let envFilePath = baseDir </> "env"
|
||||
let envFilePath = fromGHCupPath baseDir </> "env"
|
||||
|
||||
confFilePath <- getConfigFilePath
|
||||
|
||||
@ -2038,14 +2040,14 @@ rmGhcupDirs = do
|
||||
handleRm $ rmBinDir binDir
|
||||
handleRm $ rmDir recycleDir
|
||||
when isWindows $ do
|
||||
logInfo $ "removing " <> T.pack (baseDir </> "msys64")
|
||||
handleRm $ rmPathForcibly (baseDir </> "msys64")
|
||||
logInfo $ "removing " <> T.pack (fromGHCupPath baseDir </> "msys64")
|
||||
handleRm $ rmPathForcibly (baseDir `appendGHCupPath` "msys64")
|
||||
|
||||
handleRm $ removeEmptyDirsRecursive baseDir
|
||||
handleRm $ removeEmptyDirsRecursive (fromGHCupPath baseDir)
|
||||
|
||||
-- report files in baseDir that are left-over after
|
||||
-- the standard location deletions above
|
||||
hideErrorDef [doesNotExistErrorType] [] $ reportRemainingFiles baseDir
|
||||
hideErrorDef [doesNotExistErrorType] [] $ reportRemainingFiles (fromGHCupPath baseDir)
|
||||
|
||||
where
|
||||
handleRm :: (MonadReader env m, MonadCatch m, HasLog env, MonadIO m) => m () -> m ()
|
||||
@ -2062,15 +2064,15 @@ rmGhcupDirs = do
|
||||
logInfo "removing Ghcup Config File"
|
||||
hideErrorDef [permissionErrorType] () $ deleteFile' confFilePath
|
||||
|
||||
rmDir :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()
|
||||
rmDir :: (HasLog env, MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => GHCupPath -> m ()
|
||||
rmDir dir =
|
||||
-- 'getDirectoryContentsRecursive' is lazy IO. In case
|
||||
-- an error leaks through, we catch it here as well,
|
||||
-- althought 'deleteFile' should already handle it.
|
||||
hideErrorDef [doesNotExistErrorType] () $ do
|
||||
logInfo $ "removing " <> T.pack dir
|
||||
logInfo $ "removing " <> T.pack (fromGHCupPath dir)
|
||||
contents <- liftIO $ getDirectoryContentsRecursive dir
|
||||
forM_ contents (deleteFile' . (dir </>))
|
||||
forM_ contents (deleteFile' . (fromGHCupPath dir </>))
|
||||
|
||||
rmBinDir :: (MonadReader env m, HasDirs env, MonadMask m, MonadIO m, MonadCatch m) => FilePath -> m ()
|
||||
rmBinDir binDir
|
||||
@ -2085,7 +2087,7 @@ rmGhcupDirs = do
|
||||
reportRemainingFiles dir = do
|
||||
-- force the files so the errors don't leak
|
||||
(force -> !remainingFiles) <- liftIO
|
||||
(getDirectoryContentsRecursive dir >>= evaluate)
|
||||
(getDirectoryContentsRecursiveUnsafe dir >>= evaluate)
|
||||
let normalizedFilePaths = fmap normalise remainingFiles
|
||||
let sortedByDepthRemainingFiles = sortBy (flip compareFn) normalizedFilePaths
|
||||
let remainingFilesAbsolute = fmap (dir </>) sortedByDepthRemainingFiles
|
||||
@ -2113,7 +2115,7 @@ removeDirIfEmptyOrIsSymlink filepath =
|
||||
hideError UnsatisfiedConstraints $
|
||||
handleIO' InappropriateType
|
||||
(handleIfSym filepath)
|
||||
(liftIO $ rmDirectory filepath)
|
||||
(liftIO $ removeEmptyDirectory filepath)
|
||||
where
|
||||
handleIfSym fp e = do
|
||||
isSym <- liftIO $ pathIsSymbolicLink fp
|
||||
@ -2147,10 +2149,10 @@ getDebugInfo :: ( Alternative m
|
||||
DebugInfo
|
||||
getDebugInfo = do
|
||||
Dirs {..} <- lift getDirs
|
||||
let diBaseDir = baseDir
|
||||
let diBaseDir = fromGHCupPath baseDir
|
||||
let diBinDir = binDir
|
||||
diGHCDir <- lift ghcupGHCBaseDir
|
||||
let diCacheDir = cacheDir
|
||||
diGHCDir <- fromGHCupPath <$> lift ghcupGHCBaseDir
|
||||
let diCacheDir = fromGHCupPath cacheDir
|
||||
diArch <- lE getArchitecture
|
||||
diPlatform <- liftE getPlatform
|
||||
pure $ DebugInfo { .. }
|
||||
@ -2231,20 +2233,20 @@ compileGHC targetGhc ov bstrap jobs mbuildConfig patches aargs buildFlavour hadr
|
||||
|
||||
-- unpack
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ cleanUpOnError tmpUnpack (unpackToDir (fromGHCupPath tmpUnpack) dl)
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform $ fromGHCupPath tmpUnpack
|
||||
|
||||
workdir <- maybe (pure tmpUnpack)
|
||||
(liftE . intoSubdir tmpUnpack)
|
||||
(view dlSubdir dlInfo)
|
||||
liftE $ applyAnyPatch patches workdir
|
||||
liftE $ applyAnyPatch patches (fromGHCupPath workdir)
|
||||
|
||||
pure (workdir, tmpUnpack, tver)
|
||||
|
||||
-- clone from git
|
||||
Right GitBranch{..} -> do
|
||||
tmpUnpack <- lift mkGhcupTmpDir
|
||||
let git args = execLogged "git" ("--no-pager":args) (Just tmpUnpack) "git" Nothing
|
||||
let git args = execLogged "git" ("--no-pager":args) (Just $ fromGHCupPath tmpUnpack) "git" Nothing
|
||||
tver <- reThrowAll @_ @'[PatchFailed, ProcessError, NotFoundInPATH, DigestError, DownloadFailed, GPGError] DownloadFailed $ do
|
||||
let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo
|
||||
lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)"
|
||||
@ -2265,16 +2267,16 @@ compileGHC targetGhc ov bstrap jobs mbuildConfig patches aargs buildFlavour hadr
|
||||
|
||||
lEM $ git [ "checkout", "FETCH_HEAD" ]
|
||||
lEM $ git [ "submodule", "update", "--init", "--depth", "1" ]
|
||||
liftE $ applyAnyPatch patches tmpUnpack
|
||||
lEM $ execWithGhcEnv "python3" ["./boot"] (Just tmpUnpack) "ghc-bootstrap"
|
||||
lEM $ execWithGhcEnv "sh" ["./configure"] (Just tmpUnpack) "ghc-bootstrap"
|
||||
liftE $ applyAnyPatch patches (fromGHCupPath tmpUnpack)
|
||||
lEM $ execWithGhcEnv "python3" ["./boot"] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap"
|
||||
lEM $ execWithGhcEnv "sh" ["./configure"] (Just $ fromGHCupPath tmpUnpack) "ghc-bootstrap"
|
||||
CapturedProcess {..} <- lift $ makeOut
|
||||
["show!", "--quiet", "VALUE=ProjectVersion" ] (Just tmpUnpack)
|
||||
["show!", "--quiet", "VALUE=ProjectVersion" ] (Just $ fromGHCupPath tmpUnpack)
|
||||
case _exitCode of
|
||||
ExitSuccess -> throwEither . MP.parse ghcProjectVersion "" . decUTF8Safe' $ _stdOut
|
||||
ExitFailure c -> fail ("Could not figure out GHC project version. Exit code was: " <> show c <> ". Error was: " <> T.unpack (decUTF8Safe' _stdErr))
|
||||
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack
|
||||
liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform (fromGHCupPath tmpUnpack)
|
||||
lift $ logInfo $ "Git version " <> T.pack ref <> " corresponds to GHC version " <> prettyVer tver
|
||||
|
||||
pure (tmpUnpack, tmpUnpack, GHCTargetVersion Nothing tver)
|
||||
@ -2303,9 +2305,9 @@ compileGHC targetGhc ov bstrap jobs mbuildConfig patches aargs buildFlavour hadr
|
||||
tmpUnpack
|
||||
(do
|
||||
b <- if hadrian
|
||||
then compileHadrianBindist tver workdir ghcdir
|
||||
else compileMakeBindist tver workdir ghcdir
|
||||
bmk <- liftIO $ handleIO (\_ -> pure "") $ B.readFile (build_mk workdir)
|
||||
then compileHadrianBindist tver (fromGHCupPath workdir) ghcdir
|
||||
else compileMakeBindist tver (fromGHCupPath workdir) ghcdir
|
||||
bmk <- liftIO $ handleIO (\_ -> pure "") $ B.readFile (build_mk $ fromGHCupPath workdir)
|
||||
pure (b, bmk)
|
||||
)
|
||||
|
||||
@ -2500,7 +2502,7 @@ compileGHC targetGhc ov bstrap jobs mbuildConfig patches aargs buildFlavour hadr
|
||||
<> T.unpack cDigest
|
||||
<> ".tar"
|
||||
<> takeExtension tar)
|
||||
let tarPath = cacheDir </> tarName
|
||||
let tarPath = fromGHCupPath cacheDir </> tarName
|
||||
copyFileE (workdir </> tar) tarPath False
|
||||
lift $ logInfo $ "Copied bindist to " <> T.pack tarPath
|
||||
pure tarPath
|
||||
@ -2674,7 +2676,7 @@ upgradeGHCup mtarget force' fatal = do
|
||||
(Just ghcupPVPVer) <- pure $ pvpToVersion ghcUpVer ""
|
||||
when (not force' && (latestVer <= ghcupPVPVer)) $ throwE NoUpdate
|
||||
dli <- liftE $ getDownloadInfo GHCup latestVer
|
||||
tmp <- lift withGHCupTmpDir
|
||||
tmp <- fromGHCupPath <$> lift withGHCupTmpDir
|
||||
let fn = "ghcup" <> exeExt
|
||||
p <- liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmp (Just fn) False
|
||||
let destDir = takeDirectory destFile
|
||||
@ -2768,7 +2770,7 @@ whereIsTool tool ver@GHCTargetVersion {..} = do
|
||||
GHC -> do
|
||||
whenM (lift $ fmap not $ ghcInstalled ver)
|
||||
$ throwE (NotInstalled GHC ver)
|
||||
bdir <- lift $ ghcupGHCDir ver
|
||||
bdir <- fromGHCupPath <$> lift (ghcupGHCDir ver)
|
||||
pure (bdir </> "bin" </> ghcBinaryName ver)
|
||||
Cabal -> do
|
||||
whenM (lift $ fmap not $ cabalInstalled _tvVersion)
|
||||
@ -2780,7 +2782,7 @@ whereIsTool tool ver@GHCTargetVersion {..} = do
|
||||
ifM (lift $ isLegacyHLS _tvVersion)
|
||||
(pure (binDir dirs </> "haskell-language-server-wrapper-" <> T.unpack (prettyVer _tvVersion) <> exeExt))
|
||||
$ do
|
||||
bdir <- lift $ ghcupHLSDir _tvVersion
|
||||
bdir <- fromGHCupPath <$> lift (ghcupHLSDir _tvVersion)
|
||||
pure (bdir </> "bin" </> "haskell-language-server-wrapper" <> exeExt)
|
||||
|
||||
Stack -> do
|
||||
@ -2866,6 +2868,7 @@ rmProfilingLibs = do
|
||||
forM_ regexes $ \regex ->
|
||||
forM_ ghcs $ \ghc -> do
|
||||
d <- ghcupGHCDir ghc
|
||||
-- TODO: audit findFilesDeep
|
||||
matches <- liftIO $ handleIO (\_ -> pure []) $ findFilesDeep
|
||||
d
|
||||
(makeRegexOpts compExtended
|
||||
@ -2873,7 +2876,7 @@ rmProfilingLibs = do
|
||||
regex
|
||||
)
|
||||
forM_ matches $ \m -> do
|
||||
let p = d </> m
|
||||
let p = fromGHCupPath d </> m
|
||||
logDebug $ "rm " <> T.pack p
|
||||
rmFile p
|
||||
|
||||
@ -2892,8 +2895,8 @@ rmShareDir = do
|
||||
ghcs <- fmap rights getInstalledGHCs
|
||||
forM_ ghcs $ \ghc -> do
|
||||
d <- ghcupGHCDir ghc
|
||||
let p = d </> "share"
|
||||
logDebug $ "rm -rf " <> T.pack p
|
||||
let p = d `appendGHCupPath` "share"
|
||||
logDebug $ "rm -rf " <> T.pack (fromGHCupPath p)
|
||||
rmPathForcibly p
|
||||
|
||||
|
||||
@ -2938,9 +2941,9 @@ rmCache :: ( MonadReader env m
|
||||
=> m ()
|
||||
rmCache = do
|
||||
Dirs {..} <- getDirs
|
||||
contents <- liftIO $ listDirectory cacheDir
|
||||
contents <- liftIO $ listDirectory (fromGHCupPath cacheDir)
|
||||
forM_ contents $ \f -> do
|
||||
let p = cacheDir </> f
|
||||
let p = fromGHCupPath cacheDir </> f
|
||||
logDebug $ "rm " <> T.pack p
|
||||
rmFile p
|
||||
|
||||
@ -2953,17 +2956,10 @@ rmTmp :: ( MonadReader env m
|
||||
)
|
||||
=> m ()
|
||||
rmTmp = do
|
||||
tmpdir <- liftIO getCanonicalTemporaryDirectory
|
||||
ghcup_dirs <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
tmpdir
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^ghcup-.*$|] :: ByteString)
|
||||
)
|
||||
ghcup_dirs <- liftIO getGHCupTmpDirs
|
||||
forM_ ghcup_dirs $ \f -> do
|
||||
let p = tmpdir </> f
|
||||
logDebug $ "rm -rf " <> T.pack p
|
||||
rmPathForcibly p
|
||||
logDebug $ "rm -rf " <> T.pack (fromGHCupPath f)
|
||||
rmPathForcibly f
|
||||
|
||||
|
||||
applyAnyPatch :: ( MonadReader env m
|
||||
@ -2982,7 +2978,7 @@ applyAnyPatch :: ( MonadReader env m
|
||||
applyAnyPatch Nothing _ = pure ()
|
||||
applyAnyPatch (Just (Left pdir)) workdir = liftE $ applyPatches pdir workdir
|
||||
applyAnyPatch (Just (Right uris)) workdir = do
|
||||
tmpUnpack <- lift withGHCupTmpDir
|
||||
tmpUnpack <- fromGHCupPath <$> lift withGHCupTmpDir
|
||||
forM_ uris $ \uri -> do
|
||||
patch <- liftE $ download uri Nothing Nothing tmpUnpack Nothing False
|
||||
liftE $ applyPatch patch workdir
|
||||
|
@ -69,7 +69,6 @@ import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import Safe
|
||||
import System.Directory
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
import System.FilePath
|
||||
@ -145,7 +144,7 @@ getDownloadsF = do
|
||||
yamlFromCache :: (MonadReader env m, HasDirs env) => URI -> m FilePath
|
||||
yamlFromCache uri = do
|
||||
Dirs{..} <- getDirs
|
||||
pure (cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri))
|
||||
pure (fromGHCupPath cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri))
|
||||
|
||||
|
||||
etagsFile :: FilePath -> FilePath
|
||||
@ -242,7 +241,7 @@ getBase uri = do
|
||||
Settings { metaCache } <- lift getSettings
|
||||
|
||||
-- for local files, let's short-circuit and ignore access time
|
||||
if | scheme == "file" -> liftE $ download uri' Nothing Nothing cacheDir Nothing True
|
||||
if | scheme == "file" -> liftE $ download uri' Nothing Nothing (fromGHCupPath cacheDir) Nothing True
|
||||
| e -> do
|
||||
accessTime <- fmap utcTimeToPOSIXSeconds $ liftIO $ getAccessTime json_file
|
||||
let sinceLastAccess = utcTimeToPOSIXSeconds currentTime - accessTime
|
||||
@ -581,7 +580,7 @@ downloadCached dli mfn = do
|
||||
True -> downloadCached' dli mfn Nothing
|
||||
False -> do
|
||||
tmp <- lift withGHCupTmpDir
|
||||
liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmp mfn False
|
||||
liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) (fromGHCupPath tmp) mfn False
|
||||
|
||||
|
||||
downloadCached' :: ( MonadReader env m
|
||||
@ -599,7 +598,7 @@ downloadCached' :: ( MonadReader env m
|
||||
-> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath
|
||||
downloadCached' dli mfn mDestDir = do
|
||||
Dirs { cacheDir } <- lift getDirs
|
||||
let destDir = fromMaybe cacheDir mDestDir
|
||||
let destDir = fromMaybe (fromGHCupPath cacheDir) mDestDir
|
||||
let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) mfn
|
||||
let cachfile = destDir </> fn
|
||||
fileExists <- liftIO $ doesFileExist cachfile
|
||||
|
@ -23,6 +23,7 @@ import GHCup.Errors
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.File
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude
|
||||
@ -46,7 +47,6 @@ import Prelude hiding ( abs
|
||||
, writeFile
|
||||
)
|
||||
import System.Info
|
||||
import System.Directory
|
||||
import System.OsRelease
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow )
|
||||
import Text.Regex.Posix
|
||||
|
@ -26,6 +26,9 @@ module GHCup.Types
|
||||
)
|
||||
where
|
||||
|
||||
import {-# SOURCE #-} GHCup.Utils.Dirs ( fromGHCupPath )
|
||||
import {-# SOURCE #-} GHCup.Utils.Dirs ( GHCupPath )
|
||||
|
||||
import Control.DeepSeq ( NFData, rnf )
|
||||
import Data.Map.Strict ( Map )
|
||||
import Data.List.NonEmpty ( NonEmpty (..) )
|
||||
@ -438,13 +441,13 @@ defaultSettings = Settings False defaultMetaCache False Never Curl False GHCupUR
|
||||
instance NFData Settings
|
||||
|
||||
data Dirs = Dirs
|
||||
{ baseDir :: FilePath
|
||||
{ baseDir :: GHCupPath
|
||||
, binDir :: FilePath
|
||||
, cacheDir :: FilePath
|
||||
, logsDir :: FilePath
|
||||
, confDir :: FilePath
|
||||
, dbDir :: FilePath
|
||||
, recycleDir :: FilePath -- mainly used on windows
|
||||
, cacheDir :: GHCupPath
|
||||
, logsDir :: GHCupPath
|
||||
, confDir :: GHCupPath
|
||||
, dbDir :: GHCupPath
|
||||
, recycleDir :: GHCupPath -- mainly used on windows
|
||||
}
|
||||
deriving (Show, GHC.Generic)
|
||||
|
||||
@ -636,9 +639,11 @@ data InstallDir = IsolateDir FilePath
|
||||
deriving (Eq, Show)
|
||||
|
||||
data InstallDirResolved = IsolateDirResolved FilePath
|
||||
| GHCupDir FilePath
|
||||
| GHCupDir GHCupPath
|
||||
| GHCupBinDir FilePath
|
||||
deriving (Eq, Show)
|
||||
|
||||
fromInstallDir :: InstallDirResolved -> FilePath
|
||||
fromInstallDir (IsolateDirResolved fp) = fp
|
||||
fromInstallDir (GHCupDir fp) = fp
|
||||
fromInstallDir (GHCupDir fp) = fromGHCupPath fp
|
||||
fromInstallDir (GHCupBinDir fp) = fp
|
||||
|
@ -72,7 +72,6 @@ import GHC.IO.Exception
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Optics
|
||||
import Safe
|
||||
import System.Directory hiding ( findFiles, copyFile )
|
||||
import System.FilePath
|
||||
import System.IO.Error
|
||||
import Text.Regex.Posix
|
||||
@ -281,14 +280,14 @@ rmPlainHLS = do
|
||||
ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool
|
||||
ghcInstalled ver = do
|
||||
ghcdir <- ghcupGHCDir ver
|
||||
liftIO $ doesDirectoryExist ghcdir
|
||||
liftIO $ doesDirectoryExist (fromGHCupPath ghcdir)
|
||||
|
||||
|
||||
-- | Whether the given GHC version is installed from source.
|
||||
ghcSrcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool
|
||||
ghcSrcInstalled ver = do
|
||||
ghcdir <- ghcupGHCDir ver
|
||||
liftIO $ doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
||||
liftIO $ doesFileExist (fromGHCupPath ghcdir </> ghcUpSrcBuiltFile)
|
||||
|
||||
|
||||
-- | Whether the given GHC version is set as the current.
|
||||
@ -331,7 +330,7 @@ ghcSet mtarget = do
|
||||
getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath GHCTargetVersion]
|
||||
getInstalledGHCs = do
|
||||
ghcdir <- ghcupGHCBaseDir
|
||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory ghcdir
|
||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory (fromGHCupPath ghcdir)
|
||||
forM fs $ \f -> case parseGHCupGHCDir f of
|
||||
Right r -> pure $ Right r
|
||||
Left _ -> pure $ Left f
|
||||
@ -434,7 +433,7 @@ getInstalledHLSs = do
|
||||
Nothing -> pure $ Left f
|
||||
|
||||
hlsdir <- ghcupHLSBaseDir
|
||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory hlsdir
|
||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory (fromGHCupPath hlsdir)
|
||||
new <- forM fs $ \f -> case parseGHCupHLSDir f of
|
||||
Right r -> pure $ Right r
|
||||
Left _ -> pure $ Left f
|
||||
@ -519,7 +518,7 @@ hlsInstalled ver = do
|
||||
isLegacyHLS :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool
|
||||
isLegacyHLS ver = do
|
||||
bdir <- ghcupHLSDir ver
|
||||
not <$> liftIO (doesDirectoryExist bdir)
|
||||
not <$> liftIO (doesDirectoryExist $ fromGHCupPath bdir)
|
||||
|
||||
|
||||
-- Return the currently set hls version, if any.
|
||||
@ -620,7 +619,7 @@ hlsInternalServerScripts :: (MonadReader env m, HasDirs env, MonadIO m, MonadThr
|
||||
-> m [FilePath]
|
||||
hlsInternalServerScripts ver mghcVer = do
|
||||
dir <- ghcupHLSDir ver
|
||||
let bdir = dir </> "bin"
|
||||
let bdir = fromGHCupPath dir </> "bin"
|
||||
fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)
|
||||
<$> liftIO (listDirectory bdir)
|
||||
|
||||
@ -631,7 +630,7 @@ hlsInternalServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadTh
|
||||
-> Maybe Version -- ^ optional GHC version
|
||||
-> m [FilePath]
|
||||
hlsInternalServerBinaries ver mghcVer = do
|
||||
dir <- ghcupHLSDir ver
|
||||
dir <- fromGHCupPath <$> ghcupHLSDir ver
|
||||
let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)
|
||||
(Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left "bin"]
|
||||
fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)
|
||||
@ -645,7 +644,7 @@ hlsInternalServerLibs :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow
|
||||
-> Version -- ^ GHC version
|
||||
-> m [FilePath]
|
||||
hlsInternalServerLibs ver ghcVer = do
|
||||
dir <- ghcupHLSDir ver
|
||||
dir <- fromGHCupPath <$> ghcupHLSDir ver
|
||||
let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)
|
||||
(Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left ("lib" </> T.unpack (prettyVer ghcVer))]
|
||||
fmap (bdir </>) <$> liftIO (listDirectory bdir)
|
||||
@ -849,21 +848,21 @@ getArchiveFiles av = do
|
||||
|
||||
|
||||
intoSubdir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m, MonadCatch m)
|
||||
=> FilePath -- ^ unpacked tar dir
|
||||
=> GHCupPath -- ^ unpacked tar dir
|
||||
-> TarDir -- ^ how to descend
|
||||
-> Excepts '[TarDirDoesNotExist] m FilePath
|
||||
-> Excepts '[TarDirDoesNotExist] m GHCupPath
|
||||
intoSubdir bdir tardir = case tardir of
|
||||
RealDir pr -> do
|
||||
whenM (fmap not . liftIO . doesDirectoryExist $ (bdir </> pr))
|
||||
whenM (fmap not . liftIO . doesDirectoryExist $ (fromGHCupPath (bdir `appendGHCupPath` pr)))
|
||||
(throwE $ TarDirDoesNotExist tardir)
|
||||
pure (bdir </> pr)
|
||||
pure (bdir `appendGHCupPath` pr)
|
||||
RegexDir r -> do
|
||||
let rs = split (`elem` pathSeparators) r
|
||||
foldlM
|
||||
(\y x ->
|
||||
(handleIO (\_ -> pure []) . liftIO . findFiles y . regex $ x) >>= (\case
|
||||
(handleIO (\_ -> pure []) . liftIO . findFiles (fromGHCupPath y) . regex $ x) >>= (\case
|
||||
[] -> throwE $ TarDirDoesNotExist tardir
|
||||
(p : _) -> pure (y </> p)) . sort
|
||||
(p : _) -> pure (y `appendGHCupPath` p)) . sort
|
||||
)
|
||||
bdir
|
||||
rs
|
||||
@ -909,7 +908,7 @@ ghcInternalBinDir :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m,
|
||||
=> GHCTargetVersion
|
||||
-> m FilePath
|
||||
ghcInternalBinDir ver = do
|
||||
ghcdir <- ghcupGHCDir ver
|
||||
ghcdir <- fromGHCupPath <$> ghcupGHCDir ver
|
||||
pure (ghcdir </> "bin")
|
||||
|
||||
|
||||
@ -1045,7 +1044,6 @@ getChangeLog dls tool (Right tag) =
|
||||
-- | Execute a build action while potentially cleaning up:
|
||||
--
|
||||
-- 1. the build directory, depending on the KeepDirs setting
|
||||
-- 2. the install destination, depending on whether the build failed
|
||||
runBuildAction :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
, HasSettings env
|
||||
@ -1056,7 +1054,7 @@ runBuildAction :: ( MonadReader env m
|
||||
, MonadFail m
|
||||
, MonadCatch m
|
||||
)
|
||||
=> FilePath -- ^ build directory (cleaned up depending on Settings)
|
||||
=> GHCupPath -- ^ build directory (cleaned up depending on Settings)
|
||||
-> Excepts e m a
|
||||
-> Excepts e m a
|
||||
runBuildAction bdir action = do
|
||||
@ -1083,7 +1081,7 @@ cleanUpOnError :: ( MonadReader env m
|
||||
, MonadFail m
|
||||
, MonadCatch m
|
||||
)
|
||||
=> FilePath -- ^ build directory (cleaned up depending on Settings)
|
||||
=> GHCupPath -- ^ build directory (cleaned up depending on Settings)
|
||||
-> Excepts e m a
|
||||
-> Excepts e m a
|
||||
cleanUpOnError bdir action = do
|
||||
@ -1104,7 +1102,7 @@ cleanFinally :: ( MonadReader env m
|
||||
, MonadFail m
|
||||
, MonadCatch m
|
||||
)
|
||||
=> FilePath -- ^ build directory (cleaned up depending on Settings)
|
||||
=> GHCupPath -- ^ build directory (cleaned up depending on Settings)
|
||||
-> Excepts e m a
|
||||
-> Excepts e m a
|
||||
cleanFinally bdir action = do
|
||||
@ -1115,10 +1113,10 @@ cleanFinally bdir action = do
|
||||
|
||||
-- | Remove a build directory, ignoring if it doesn't exist and gracefully
|
||||
-- printing other errors without crashing.
|
||||
rmBDir :: (MonadReader env m, HasLog env, MonadUnliftIO m, MonadIO m) => FilePath -> m ()
|
||||
rmBDir :: (MonadReader env m, HasLog env, MonadUnliftIO m, MonadIO m) => GHCupPath -> m ()
|
||||
rmBDir dir = withRunInIO (\run -> run $
|
||||
liftIO $ handleIO (\e -> run $ logWarn $
|
||||
"Couldn't remove build dir " <> T.pack dir <> ", error was: " <> T.pack (displayException e))
|
||||
"Couldn't remove build dir " <> T.pack (fromGHCupPath dir) <> ", error was: " <> T.pack (displayException e))
|
||||
$ hideError doesNotExistErrorType
|
||||
$ rmPathForcibly dir)
|
||||
|
||||
@ -1204,7 +1202,7 @@ createLink :: ( MonadMask m
|
||||
createLink link exe
|
||||
| isWindows = do
|
||||
dirs <- getDirs
|
||||
let shimGen = cacheDir dirs </> "gs.exe"
|
||||
let shimGen = fromGHCupPath (cacheDir dirs) </> "gs.exe"
|
||||
|
||||
let shim = dropExtension exe <.> "shim"
|
||||
-- For hardlinks, link needs to be absolute.
|
||||
@ -1248,8 +1246,8 @@ ensureGlobalTools
|
||||
let dl = downloadCached' shimDownload (Just "gs.exe") Nothing
|
||||
void $ (\DigestError{} -> do
|
||||
lift $ logWarn "Digest doesn't match, redownloading gs.exe..."
|
||||
lift $ logDebug ("rm -f " <> T.pack (cacheDir dirs </> "gs.exe"))
|
||||
lift $ hideError doesNotExistErrorType $ recycleFile (cacheDir dirs </> "gs.exe")
|
||||
lift $ logDebug ("rm -f " <> T.pack (fromGHCupPath (cacheDir dirs) </> "gs.exe"))
|
||||
lift $ hideError doesNotExistErrorType $ recycleFile (fromGHCupPath (cacheDir dirs) </> "gs.exe")
|
||||
liftE @'[GPGError, DigestError , DownloadFailed] $ dl
|
||||
) `catchE` liftE @'[GPGError, DigestError , DownloadFailed] dl
|
||||
| otherwise = pure ()
|
||||
@ -1258,14 +1256,14 @@ ensureGlobalTools
|
||||
-- | Ensure ghcup directory structure exists.
|
||||
ensureDirectories :: Dirs -> IO ()
|
||||
ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir) = do
|
||||
createDirRecursive' baseDir
|
||||
createDirRecursive' (baseDir </> "ghc")
|
||||
createDirRecursive' (fromGHCupPath baseDir)
|
||||
createDirRecursive' (fromGHCupPath baseDir </> "ghc")
|
||||
createDirRecursive' binDir
|
||||
createDirRecursive' cacheDir
|
||||
createDirRecursive' logsDir
|
||||
createDirRecursive' confDir
|
||||
createDirRecursive' trashDir
|
||||
createDirRecursive' dbDir
|
||||
createDirRecursive' (fromGHCupPath cacheDir)
|
||||
createDirRecursive' (fromGHCupPath logsDir)
|
||||
createDirRecursive' (fromGHCupPath confDir)
|
||||
createDirRecursive' (fromGHCupPath trashDir)
|
||||
createDirRecursive' (fromGHCupPath dbDir)
|
||||
pure ()
|
||||
|
||||
|
||||
@ -1293,7 +1291,7 @@ installDestSanityCheck :: ( MonadIO m
|
||||
Excepts '[DirNotEmpty] m ()
|
||||
installDestSanityCheck (IsolateDirResolved isoDir) = do
|
||||
hideErrorDef [doesNotExistErrorType] () $ do
|
||||
contents <- liftIO $ getDirectoryContentsRecursive isoDir
|
||||
contents <- liftIO $ getDirectoryContentsRecursiveUnsafe isoDir
|
||||
unless (null contents) (throwE $ DirNotEmpty isoDir)
|
||||
installDestSanityCheck _ = pure ()
|
||||
|
||||
@ -1342,6 +1340,6 @@ recordedInstallationFile :: ( MonadReader env m
|
||||
-> m FilePath
|
||||
recordedInstallationFile t v' = do
|
||||
Dirs {..} <- getDirs
|
||||
pure (dbDir </> prettyShow t </> T.unpack (tVerToText v'))
|
||||
pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack (tVerToText v'))
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Utils.Dirs
|
||||
@ -30,6 +31,74 @@ module GHCup.Utils.Dirs
|
||||
, getConfigFilePath
|
||||
, useXDG
|
||||
, cleanupTrash
|
||||
|
||||
, GHCupPath
|
||||
, appendGHCupPath
|
||||
, fromGHCupPath
|
||||
, createTempGHCupDirectory
|
||||
, getGHCupTmpDirs
|
||||
|
||||
, removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
|
||||
-- System.Directory re-exports
|
||||
, createDirectory
|
||||
, createDirectoryIfMissing
|
||||
, renameDirectory
|
||||
, listDirectory
|
||||
, getDirectoryContents
|
||||
, getCurrentDirectory
|
||||
, setCurrentDirectory
|
||||
, withCurrentDirectory
|
||||
, getHomeDirectory
|
||||
, XdgDirectory(..)
|
||||
, getXdgDirectory
|
||||
, XdgDirectoryList(..)
|
||||
, getXdgDirectoryList
|
||||
, getAppUserDataDirectory
|
||||
, getUserDocumentsDirectory
|
||||
, getTemporaryDirectory
|
||||
, removeFile
|
||||
, renameFile
|
||||
, renamePath
|
||||
, getFileSize
|
||||
, canonicalizePath
|
||||
, makeAbsolute
|
||||
, makeRelativeToCurrentDirectory
|
||||
, doesPathExist
|
||||
, doesFileExist
|
||||
, doesDirectoryExist
|
||||
, findExecutable
|
||||
, findExecutables
|
||||
, findExecutablesInDirectories
|
||||
, findFile
|
||||
, findFileWith
|
||||
, findFilesWith
|
||||
, exeExtension
|
||||
, createFileLink
|
||||
, createDirectoryLink
|
||||
, removeDirectoryLink
|
||||
, pathIsSymbolicLink
|
||||
, getSymbolicLinkTarget
|
||||
, Permissions
|
||||
, emptyPermissions
|
||||
, readable
|
||||
, writable
|
||||
, executable
|
||||
, searchable
|
||||
, setOwnerReadable
|
||||
, setOwnerWritable
|
||||
, setOwnerExecutable
|
||||
, setOwnerSearchable
|
||||
, getPermissions
|
||||
, setPermissions
|
||||
, copyPermissions
|
||||
, getAccessTime
|
||||
, getModificationTime
|
||||
, setAccessTime
|
||||
, setModificationTime
|
||||
, isSymbolicLink
|
||||
)
|
||||
where
|
||||
|
||||
@ -41,23 +110,35 @@ import GHCup.Types.Optics
|
||||
import GHCup.Utils.MegaParsec
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.File.Common
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
import Control.DeepSeq (NFData, rnf)
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
import Control.Monad.IO.Unlift
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource hiding (throwM)
|
||||
import Data.List
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.Bifunctor
|
||||
import Data.Maybe
|
||||
import Data.Versions
|
||||
import GHC.IO.Exception ( IOErrorType(NoSuchThing) )
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Optics
|
||||
import System.Directory
|
||||
import System.Directory hiding ( removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
, findFiles
|
||||
)
|
||||
import qualified System.Directory as SD
|
||||
|
||||
import System.DiskSpace
|
||||
import System.Environment
|
||||
import System.FilePath
|
||||
import System.IO.Temp
|
||||
import Text.Regex.Posix
|
||||
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.Text as T
|
||||
@ -67,6 +148,41 @@ import Control.Concurrent (threadDelay)
|
||||
|
||||
|
||||
|
||||
---------------------------
|
||||
--[ GHCupPath utilities ]--
|
||||
---------------------------
|
||||
|
||||
-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.
|
||||
--
|
||||
-- The constructor is not exported.
|
||||
newtype GHCupPath = GHCupPath FilePath
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
instance NFData GHCupPath where
|
||||
rnf (GHCupPath fp) = rnf fp
|
||||
|
||||
appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath
|
||||
appendGHCupPath (GHCupPath gp) fp = GHCupPath (gp </> fp)
|
||||
|
||||
fromGHCupPath :: GHCupPath -> FilePath
|
||||
fromGHCupPath (GHCupPath gp) = gp
|
||||
|
||||
createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath
|
||||
createTempGHCupDirectory (GHCupPath gp) d = GHCupPath <$> createTempDirectory gp d
|
||||
|
||||
|
||||
getGHCupTmpDirs :: IO [GHCupPath]
|
||||
getGHCupTmpDirs = do
|
||||
tmpdir <- getCanonicalTemporaryDirectory
|
||||
ghcup_dirs <- handleIO (\_ -> pure []) $ findFiles
|
||||
tmpdir
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^ghcup-.*$|] :: ByteString)
|
||||
)
|
||||
pure (fmap (\p -> GHCupPath (tmpdir </> p)) $ filter (("ghcup-" `isPrefixOf`) . takeDirectory) $ ghcup_dirs)
|
||||
|
||||
|
||||
------------------------------
|
||||
--[ GHCup base directories ]--
|
||||
------------------------------
|
||||
@ -76,11 +192,11 @@ import Control.Concurrent (threadDelay)
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.
|
||||
ghcupBaseDir :: IO FilePath
|
||||
ghcupBaseDir :: IO GHCupPath
|
||||
ghcupBaseDir
|
||||
| isWindows = do
|
||||
bdir <- fromMaybe "C:\\" <$> lookupEnv "GHCUP_INSTALL_BASE_PREFIX"
|
||||
pure (bdir </> "ghcup")
|
||||
pure (GHCupPath (bdir </> "ghcup"))
|
||||
| otherwise = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
@ -90,19 +206,19 @@ ghcupBaseDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".local" </> "share")
|
||||
pure (bdir </> "ghcup")
|
||||
pure (GHCupPath (bdir </> "ghcup"))
|
||||
else do
|
||||
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||
Just r -> pure r
|
||||
Nothing -> liftIO getHomeDirectory
|
||||
pure (bdir </> ".ghcup")
|
||||
pure (GHCupPath (bdir </> ".ghcup"))
|
||||
|
||||
|
||||
-- | ~/.ghcup by default
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.
|
||||
ghcupConfigDir :: IO FilePath
|
||||
ghcupConfigDir :: IO GHCupPath
|
||||
ghcupConfigDir
|
||||
| isWindows = ghcupBaseDir
|
||||
| otherwise = do
|
||||
@ -114,12 +230,12 @@ ghcupConfigDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".config")
|
||||
pure (bdir </> "ghcup")
|
||||
pure (GHCupPath (bdir </> "ghcup"))
|
||||
else do
|
||||
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||
Just r -> pure r
|
||||
Nothing -> liftIO getHomeDirectory
|
||||
pure (bdir </> ".ghcup")
|
||||
pure (GHCupPath (bdir </> ".ghcup"))
|
||||
|
||||
|
||||
-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
@ -127,7 +243,7 @@ ghcupConfigDir
|
||||
-- (which, sadly is not strictly xdg spec).
|
||||
ghcupBinDir :: IO FilePath
|
||||
ghcupBinDir
|
||||
| isWindows = ghcupBaseDir <&> (</> "bin")
|
||||
| isWindows = (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")
|
||||
| otherwise = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
@ -137,16 +253,16 @@ ghcupBinDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".local" </> "bin")
|
||||
else ghcupBaseDir <&> (</> "bin")
|
||||
else (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin")
|
||||
|
||||
|
||||
-- | Defaults to '~/.ghcup/cache'.
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.
|
||||
ghcupCacheDir :: IO FilePath
|
||||
ghcupCacheDir :: IO GHCupPath
|
||||
ghcupCacheDir
|
||||
| isWindows = ghcupBaseDir <&> (</> "cache")
|
||||
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))
|
||||
| otherwise = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
@ -156,17 +272,17 @@ ghcupCacheDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".cache")
|
||||
pure (bdir </> "ghcup")
|
||||
else ghcupBaseDir <&> (</> "cache")
|
||||
pure (GHCupPath (bdir </> "ghcup"))
|
||||
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache"))
|
||||
|
||||
|
||||
-- | Defaults to '~/.ghcup/logs'.
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.
|
||||
ghcupLogsDir :: IO FilePath
|
||||
ghcupLogsDir :: IO GHCupPath
|
||||
ghcupLogsDir
|
||||
| isWindows = ghcupBaseDir <&> (</> "logs")
|
||||
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))
|
||||
| otherwise = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
@ -176,17 +292,17 @@ ghcupLogsDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".cache")
|
||||
pure (bdir </> "ghcup" </> "logs")
|
||||
else ghcupBaseDir <&> (</> "logs")
|
||||
pure (GHCupPath (bdir </> "ghcup" </> "logs"))
|
||||
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs"))
|
||||
|
||||
|
||||
-- | Defaults to '~/.ghcup/db.
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CACHE_HOME/ghcup/db as per xdg spec.
|
||||
ghcupDbDir :: IO FilePath
|
||||
ghcupDbDir :: IO GHCupPath
|
||||
ghcupDbDir
|
||||
| isWindows = ghcupBaseDir <&> (</> "db")
|
||||
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))
|
||||
| otherwise = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
@ -196,14 +312,14 @@ ghcupDbDir
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> ".cache")
|
||||
pure (bdir </> "ghcup" </> "db")
|
||||
else ghcupBaseDir <&> (</> "db")
|
||||
pure (GHCupPath (bdir </> "ghcup" </> "db"))
|
||||
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))
|
||||
|
||||
|
||||
-- | '~/.ghcup/trash'.
|
||||
-- Mainly used on windows to improve file removal operations
|
||||
ghcupRecycleDir :: IO FilePath
|
||||
ghcupRecycleDir = ghcupBaseDir <&> (</> "trash")
|
||||
ghcupRecycleDir :: IO GHCupPath
|
||||
ghcupRecycleDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "trash"))
|
||||
|
||||
|
||||
|
||||
@ -227,7 +343,7 @@ getAllDirs = do
|
||||
getConfigFilePath :: (MonadIO m) => m FilePath
|
||||
getConfigFilePath = do
|
||||
confDir <- liftIO ghcupConfigDir
|
||||
pure $ confDir </> "config.yaml"
|
||||
pure $ fromGHCupPath confDir </> "config.yaml"
|
||||
|
||||
ghcupConfigFile :: (MonadIO m)
|
||||
=> Excepts '[JSONError] m UserSettings
|
||||
@ -245,10 +361,10 @@ ghcupConfigFile = do
|
||||
|
||||
|
||||
-- | ~/.ghcup/ghc by default.
|
||||
ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m FilePath
|
||||
ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath
|
||||
ghcupGHCBaseDir = do
|
||||
Dirs {..} <- getDirs
|
||||
pure (baseDir </> "ghc")
|
||||
pure (baseDir `appendGHCupPath` "ghc")
|
||||
|
||||
|
||||
-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.
|
||||
@ -257,11 +373,11 @@ ghcupGHCBaseDir = do
|
||||
-- * 8.8.4
|
||||
ghcupGHCDir :: (MonadReader env m, HasDirs env, MonadThrow m)
|
||||
=> GHCTargetVersion
|
||||
-> m FilePath
|
||||
-> m GHCupPath
|
||||
ghcupGHCDir ver = do
|
||||
ghcbasedir <- ghcupGHCBaseDir
|
||||
let verdir = T.unpack $ tVerToText ver
|
||||
pure (ghcbasedir </> verdir)
|
||||
pure (ghcbasedir `appendGHCupPath` verdir)
|
||||
|
||||
|
||||
-- | See 'ghcupToolParser'.
|
||||
@ -274,19 +390,19 @@ parseGHCupHLSDir (T.pack -> fp) =
|
||||
throwEither $ MP.parse version' "" fp
|
||||
|
||||
-- | ~/.ghcup/hls by default, for new-style installs.
|
||||
ghcupHLSBaseDir :: (MonadReader env m, HasDirs env) => m FilePath
|
||||
ghcupHLSBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath
|
||||
ghcupHLSBaseDir = do
|
||||
Dirs {..} <- getDirs
|
||||
pure (baseDir </> "hls")
|
||||
pure (baseDir `appendGHCupPath` "hls")
|
||||
|
||||
-- | Gets '~/.ghcup/hls/<hls-ver>' for new-style installs.
|
||||
ghcupHLSDir :: (MonadReader env m, HasDirs env, MonadThrow m)
|
||||
=> Version
|
||||
-> m FilePath
|
||||
-> m GHCupPath
|
||||
ghcupHLSDir ver = do
|
||||
basedir <- ghcupHLSBaseDir
|
||||
let verdir = T.unpack $ prettyVer ver
|
||||
pure (basedir </> verdir)
|
||||
pure (basedir `appendGHCupPath` verdir)
|
||||
|
||||
mkGhcupTmpDir :: ( MonadReader env m
|
||||
, HasDirs env
|
||||
@ -296,8 +412,8 @@ mkGhcupTmpDir :: ( MonadReader env m
|
||||
, MonadThrow m
|
||||
, MonadMask m
|
||||
, MonadIO m)
|
||||
=> m FilePath
|
||||
mkGhcupTmpDir = do
|
||||
=> m GHCupPath
|
||||
mkGhcupTmpDir = GHCupPath <$> do
|
||||
tmpdir <- liftIO getCanonicalTemporaryDirectory
|
||||
|
||||
let minSpace = 5000 -- a rough guess, aight?
|
||||
@ -333,14 +449,14 @@ withGHCupTmpDir :: ( MonadReader env m
|
||||
, MonadThrow m
|
||||
, MonadMask m
|
||||
, MonadIO m)
|
||||
=> m FilePath
|
||||
=> m GHCupPath
|
||||
withGHCupTmpDir = snd <$> withRunInIO (\run ->
|
||||
run
|
||||
$ allocate
|
||||
(run mkGhcupTmpDir)
|
||||
(\fp ->
|
||||
handleIO (\e -> run
|
||||
$ logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e)))
|
||||
$ logDebug ("Resource cleanup failed for " <> T.pack (fromGHCupPath fp) <> ", error was: " <> T.pack (displayException e)))
|
||||
. rmPathForcibly
|
||||
$ fp))
|
||||
|
||||
@ -381,12 +497,27 @@ cleanupTrash :: ( MonadIO m
|
||||
=> m ()
|
||||
cleanupTrash = do
|
||||
Dirs { recycleDir } <- getDirs
|
||||
contents <- liftIO $ listDirectory recycleDir
|
||||
contents <- liftIO $ listDirectory (fromGHCupPath recycleDir)
|
||||
if null contents
|
||||
then pure ()
|
||||
else do
|
||||
logWarn ("Removing leftover files in " <> T.pack recycleDir)
|
||||
logWarn ("Removing leftover files in " <> T.pack (fromGHCupPath recycleDir))
|
||||
forM_ contents (\fp -> handleIO (\e ->
|
||||
logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e))
|
||||
) $ liftIO $ removePathForcibly (recycleDir </> fp))
|
||||
) $ liftIO $ removePathForcibly (recycleDir `appendGHCupPath` fp))
|
||||
|
||||
|
||||
|
||||
-- System.Directory re-exports with GHCupPath
|
||||
|
||||
removeDirectory :: GHCupPath -> IO ()
|
||||
removeDirectory (GHCupPath fp) = SD.removeDirectory fp
|
||||
|
||||
removeDirectoryRecursive :: GHCupPath -> IO ()
|
||||
removeDirectoryRecursive (GHCupPath fp) = SD.removeDirectoryRecursive fp
|
||||
|
||||
removePathForcibly :: GHCupPath -> IO ()
|
||||
removePathForcibly (GHCupPath fp) = SD.removePathForcibly fp
|
||||
|
||||
|
||||
|
||||
|
37
lib/GHCup/Utils/Dirs.hs-boot
Normal file
37
lib/GHCup/Utils/Dirs.hs-boot
Normal file
@ -0,0 +1,37 @@
|
||||
module GHCup.Utils.Dirs
|
||||
( GHCupPath
|
||||
, appendGHCupPath
|
||||
, fromGHCupPath
|
||||
, createTempGHCupDirectory
|
||||
, removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
)
|
||||
where
|
||||
|
||||
import Control.DeepSeq (NFData)
|
||||
|
||||
|
||||
-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.
|
||||
newtype GHCupPath = GHCupPath FilePath
|
||||
|
||||
instance Show GHCupPath where
|
||||
|
||||
instance Eq GHCupPath where
|
||||
|
||||
instance Ord GHCupPath where
|
||||
|
||||
instance NFData GHCupPath where
|
||||
|
||||
appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath
|
||||
|
||||
fromGHCupPath :: GHCupPath -> FilePath
|
||||
|
||||
createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath
|
||||
|
||||
removeDirectory :: GHCupPath -> IO ()
|
||||
|
||||
removeDirectoryRecursive :: GHCupPath -> IO ()
|
||||
|
||||
removePathForcibly :: GHCupPath -> IO ()
|
||||
|
@ -19,6 +19,7 @@ module GHCup.Utils.File (
|
||||
#endif
|
||||
) where
|
||||
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.File.Common
|
||||
#if IS_WINDOWS
|
||||
import GHCup.Utils.File.Windows
|
||||
@ -32,7 +33,6 @@ import GHC.IO ( evaluate )
|
||||
import Control.Exception.Safe
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Control.Monad.Reader
|
||||
import System.Directory hiding (findFiles, copyFile)
|
||||
import System.FilePath
|
||||
|
||||
import Data.List (nub)
|
||||
@ -42,9 +42,9 @@ import Control.DeepSeq (force)
|
||||
|
||||
-- | Like 'mergeFileTree', except reads the entire source base dir to determine files to copy recursively.
|
||||
mergeFileTreeAll :: MonadIO m
|
||||
=> FilePath -- ^ source base directory from which to install findFiles
|
||||
=> GHCupPath -- ^ source base directory from which to install findFiles
|
||||
-> FilePath -- ^ destination base dir
|
||||
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
|
||||
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
|
||||
-> m [FilePath]
|
||||
mergeFileTreeAll sourceBase destBase copyOp = do
|
||||
(force -> !sourceFiles) <- liftIO
|
||||
@ -54,12 +54,12 @@ mergeFileTreeAll sourceBase destBase copyOp = do
|
||||
|
||||
|
||||
mergeFileTree :: MonadIO m
|
||||
=> FilePath -- ^ source base directory from which to install findFiles
|
||||
=> GHCupPath -- ^ source base directory from which to install findFiles
|
||||
-> [FilePath] -- ^ relative filepaths from source base directory
|
||||
-> FilePath -- ^ destination base dir
|
||||
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
|
||||
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
|
||||
-> m ()
|
||||
mergeFileTree sourceBase sources destBase copyOp = do
|
||||
mergeFileTree (fromGHCupPath -> sourceBase) sources destBase copyOp = do
|
||||
-- These checks are not atomic, but we perform them to have
|
||||
-- the opportunity to abort before copying has started.
|
||||
--
|
||||
|
@ -9,6 +9,7 @@ module GHCup.Utils.File.Common (
|
||||
) where
|
||||
|
||||
import GHCup.Utils.Prelude
|
||||
import {-# SOURCE #-} GHCup.Utils.Dirs ( GHCupPath )
|
||||
import GHCup.Types(ProcessError(..), CapturedProcess(..))
|
||||
|
||||
import Control.Monad.Reader
|
||||
@ -16,7 +17,11 @@ import Data.Maybe
|
||||
import Data.Text ( Text )
|
||||
import Data.Void
|
||||
import GHC.IO.Exception
|
||||
import System.Directory hiding (findFiles, copyFile)
|
||||
import System.Directory hiding ( removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
, findFiles
|
||||
)
|
||||
import System.FilePath
|
||||
import Text.Regex.Posix
|
||||
|
||||
@ -94,7 +99,7 @@ findFiles path regex = do
|
||||
contents <- listDirectory path
|
||||
pure $ filter (match regex) contents
|
||||
|
||||
findFilesDeep :: FilePath -> Regex -> IO [FilePath]
|
||||
findFilesDeep :: GHCupPath -> Regex -> IO [FilePath]
|
||||
findFilesDeep path regex = do
|
||||
contents <- getDirectoryContentsRecursive path
|
||||
pure $ filter (match regex) contents
|
||||
|
@ -17,6 +17,7 @@ Some of these functions use sophisticated logging.
|
||||
-}
|
||||
module GHCup.Utils.File.Posix where
|
||||
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.File.Common
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.Logger
|
||||
@ -42,7 +43,6 @@ import GHC.IO.Exception
|
||||
import System.IO ( stderr, hClose, hSetBinaryMode )
|
||||
import System.IO.Error
|
||||
import System.FilePath
|
||||
import System.Directory hiding ( copyFile )
|
||||
import System.Posix.Directory
|
||||
import System.Posix.Error ( throwErrnoPathIfMinus1Retry )
|
||||
import System.Posix.Internals ( withFilePath )
|
||||
@ -56,6 +56,7 @@ import qualified Control.Exception as EX
|
||||
import qualified Data.Sequence as Sq
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import qualified System.Posix.Directory as PD
|
||||
import qualified System.Posix.Files as PF
|
||||
import qualified System.Posix.Process as SPP
|
||||
import qualified System.Posix.IO as SPI
|
||||
@ -101,7 +102,7 @@ execLogged exe args chdir lfile env = do
|
||||
Settings {..} <- getSettings
|
||||
Dirs {..} <- getDirs
|
||||
logDebug $ T.pack $ "Running " <> exe <> " with arguments " <> show args
|
||||
let logfile = logsDir </> lfile <> ".log"
|
||||
let logfile = fromGHCupPath logsDir </> lfile <> ".log"
|
||||
liftIO $ bracket (openFd logfile WriteOnly (Just newFilePerms) defaultFileFlags{ append = True })
|
||||
closeFd
|
||||
(action verbose noColor)
|
||||
@ -550,3 +551,6 @@ install from to fail' = do
|
||||
| PF.isSymbolicLink fs = recreateSymlink from to fail'
|
||||
| otherwise = ioError $ mkIOError illegalOperationErrorType "install: not a regular file or symlink" Nothing (Just from)
|
||||
|
||||
|
||||
removeEmptyDirectory :: FilePath -> IO ()
|
||||
removeEmptyDirectory = PD.removeDirectory
|
||||
|
@ -17,7 +17,7 @@ Some of these functions use sophisticated logging.
|
||||
module GHCup.Utils.File.Windows where
|
||||
|
||||
import {-# SOURCE #-} GHCup.Utils ( getLinkTarget, pathIsLink )
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.Dirs hiding ( copyFile )
|
||||
import GHCup.Utils.File.Common
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Types
|
||||
@ -32,7 +32,6 @@ import Data.List
|
||||
import Foreign.C.Error
|
||||
import GHC.IO.Exception
|
||||
import GHC.IO.Handle
|
||||
import System.Directory hiding ( copyFile )
|
||||
import System.Environment
|
||||
import System.FilePath
|
||||
import System.IO
|
||||
@ -284,3 +283,6 @@ deleteFile = WS.deleteFile
|
||||
|
||||
install :: FilePath -> FilePath -> Bool -> IO ()
|
||||
install = copyFile
|
||||
|
||||
removeEmptyDirectory :: FilePath -> IO ()
|
||||
removeEmptyDirectory = WS.removeDirectory
|
||||
|
@ -17,6 +17,7 @@ module GHCup.Utils.Logger where
|
||||
|
||||
import GHCup.Types
|
||||
import GHCup.Types.Optics
|
||||
import {-# SOURCE #-} GHCup.Utils.Dirs (fromGHCupPath)
|
||||
import {-# SOURCE #-} GHCup.Utils.File.Common (findFiles)
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
@ -117,14 +118,14 @@ initGHCupFileLogging :: ( MonadReader env m
|
||||
) => m FilePath
|
||||
initGHCupFileLogging = do
|
||||
Dirs { logsDir } <- getDirs
|
||||
let logfile = logsDir </> "ghcup.log"
|
||||
let logfile = fromGHCupPath logsDir </> "ghcup.log"
|
||||
logFiles <- liftIO $ findFiles
|
||||
logsDir
|
||||
(fromGHCupPath logsDir)
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^.*\.log$|] :: B.ByteString)
|
||||
)
|
||||
forM_ logFiles $ hideError doesNotExistErrorType . recycleFile . (logsDir </>)
|
||||
forM_ logFiles $ hideError doesNotExistErrorType . recycleFile . (fromGHCupPath logsDir </>)
|
||||
|
||||
liftIO $ writeFile logfile ""
|
||||
pure logfile
|
||||
|
@ -27,6 +27,7 @@ module GHCup.Utils.Prelude
|
||||
)
|
||||
where
|
||||
|
||||
import {-# SOURCE #-} GHCup.Utils.Dirs (GHCupPath, fromGHCupPath, createTempGHCupDirectory, appendGHCupPath, removePathForcibly, removeDirectory)
|
||||
import GHCup.Types
|
||||
import GHCup.Errors
|
||||
import GHCup.Types.Optics
|
||||
@ -44,9 +45,8 @@ import Control.Monad.IO.Class
|
||||
import Control.Monad.Reader
|
||||
import Data.Bifunctor
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.List ( nub, intercalate, stripPrefix, isPrefixOf, dropWhileEnd, intersperse )
|
||||
import Data.List ( intercalate, stripPrefix, isPrefixOf, dropWhileEnd, intersperse )
|
||||
import Data.Maybe
|
||||
import Data.Foldable
|
||||
import Data.List.NonEmpty ( NonEmpty( (:|) ))
|
||||
import Data.String
|
||||
import Data.Text ( Text )
|
||||
@ -56,9 +56,12 @@ import Haskus.Utils.Types.List
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import Text.PrettyPrint.HughesPJClass ( prettyShow, Pretty )
|
||||
import System.IO.Error
|
||||
import System.IO.Temp
|
||||
import System.IO.Unsafe
|
||||
import System.Directory hiding ( copyFile )
|
||||
import System.Directory hiding ( removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
, copyFile
|
||||
)
|
||||
import System.FilePath
|
||||
|
||||
import Control.Retry
|
||||
@ -397,30 +400,6 @@ createDirRecursive' p =
|
||||
_ -> throwIO e
|
||||
|
||||
|
||||
-- | Recursively copy the contents of one directory to another path.
|
||||
--
|
||||
-- This is a rip-off of Cabal library.
|
||||
copyDirectoryRecursive :: FilePath -> FilePath -> (FilePath -> FilePath -> IO ()) -> IO ()
|
||||
copyDirectoryRecursive srcDir destDir doCopy = do
|
||||
srcFiles <- getDirectoryContentsRecursive srcDir
|
||||
copyFilesWith destDir [ (srcDir, f)
|
||||
| f <- srcFiles ]
|
||||
where
|
||||
-- | Common implementation of 'copyFiles', 'installOrdinaryFiles',
|
||||
-- 'installExecutableFiles' and 'installMaybeExecutableFiles'.
|
||||
copyFilesWith :: FilePath -> [(FilePath, FilePath)] -> IO ()
|
||||
copyFilesWith targetDir srcFiles = do
|
||||
|
||||
-- Create parent directories for everything
|
||||
let dirs = map (targetDir </>) . nub . map takeDirectory $ fmap snd srcFiles
|
||||
traverse_ (createDirectoryIfMissing True) dirs
|
||||
|
||||
-- Copy all the files
|
||||
sequence_ [ let src = srcBase </> srcFile
|
||||
dest = targetDir </> srcFile
|
||||
in doCopy src dest
|
||||
| (srcBase, srcFile) <- srcFiles ]
|
||||
|
||||
|
||||
-- | List all the files in a directory and all subdirectories.
|
||||
--
|
||||
@ -429,8 +408,12 @@ copyDirectoryRecursive srcDir destDir doCopy = do
|
||||
-- the source directory structure changes before the list is used.
|
||||
--
|
||||
-- TODO: use streamly
|
||||
getDirectoryContentsRecursive :: FilePath -> IO [FilePath]
|
||||
getDirectoryContentsRecursive topdir = recurseDirectories [""]
|
||||
getDirectoryContentsRecursive :: GHCupPath -> IO [FilePath]
|
||||
getDirectoryContentsRecursive (fromGHCupPath -> topdir) = getDirectoryContentsRecursiveUnsafe topdir
|
||||
|
||||
|
||||
getDirectoryContentsRecursiveUnsafe :: FilePath -> IO [FilePath]
|
||||
getDirectoryContentsRecursiveUnsafe topdir = recurseDirectories [""]
|
||||
where
|
||||
recurseDirectories :: [FilePath] -> IO [FilePath]
|
||||
recurseDirectories [] = return []
|
||||
@ -464,14 +447,14 @@ recyclePathForcibly :: ( MonadIO m
|
||||
, HasDirs env
|
||||
, MonadMask m
|
||||
)
|
||||
=> FilePath
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
recyclePathForcibly fp
|
||||
| isWindows = do
|
||||
Dirs { recycleDir } <- getDirs
|
||||
tmp <- liftIO $ createTempDirectory recycleDir "recyclePathForcibly"
|
||||
let dest = tmp </> takeFileName fp
|
||||
liftIO (moveFile fp dest)
|
||||
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recyclePathForcibly"
|
||||
let dest = tmp `appendGHCupPath` takeFileName (fromGHCupPath fp)
|
||||
liftIO (moveFile (fromGHCupPath fp) (fromGHCupPath dest))
|
||||
`catch`
|
||||
(\e -> if | isDoesNotExistError e -> pure ()
|
||||
| isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} -> recover (liftIO $ removePathForcibly fp)
|
||||
@ -484,7 +467,7 @@ recyclePathForcibly fp
|
||||
rmPathForcibly :: ( MonadIO m
|
||||
, MonadMask m
|
||||
)
|
||||
=> FilePath
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
rmPathForcibly fp
|
||||
| isWindows = recover (liftIO $ removePathForcibly fp)
|
||||
@ -492,7 +475,7 @@ rmPathForcibly fp
|
||||
|
||||
|
||||
rmDirectory :: (MonadIO m, MonadMask m)
|
||||
=> FilePath
|
||||
=> GHCupPath
|
||||
-> m ()
|
||||
rmDirectory fp
|
||||
| isWindows = recover (liftIO $ removeDirectory fp)
|
||||
@ -512,11 +495,11 @@ recycleFile fp
|
||||
| isWindows = do
|
||||
Dirs { recycleDir } <- getDirs
|
||||
liftIO $ whenM (doesDirectoryExist fp) $ ioError (IOError Nothing InappropriateType "recycleFile" "" Nothing (Just fp))
|
||||
tmp <- liftIO $ createTempDirectory recycleDir "recycleFile"
|
||||
let dest = tmp </> takeFileName fp
|
||||
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recycleFile"
|
||||
let dest = fromGHCupPath tmp </> takeFileName fp
|
||||
liftIO (moveFile fp dest)
|
||||
`catch`
|
||||
(\e -> if isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} then recover (liftIO $ removePathForcibly fp) else throwIO e)
|
||||
(\e -> if isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} then recover (liftIO $ rmFile fp) else throwIO e)
|
||||
`finally`
|
||||
liftIO (handleIO (\_ -> pure ()) $ removePathForcibly tmp)
|
||||
| otherwise = liftIO $ removeFile fp
|
||||
|
@ -1,6 +1,10 @@
|
||||
module GHCup.Utils.Prelude.Posix where
|
||||
|
||||
import System.Directory
|
||||
import System.Directory hiding ( removeDirectory
|
||||
, removeDirectoryRecursive
|
||||
, removePathForcibly
|
||||
, findFiles
|
||||
)
|
||||
import System.Posix.Files
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user