Compare commits
4 Commits
2d51ad8940
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 673bfef443 | |||
| b87d252fec | |||
| 18f891f261 | |||
| b2a7da29cf |
13
README.md
13
README.md
@@ -1,28 +1,25 @@
|
|||||||
# ghcup
|
# ghcup
|
||||||
|
|
||||||
A rewrite of ghcup in haskell. This can be used as a library
|
A rewrite of ghcup in haskell.
|
||||||
and may be redistributed as a binary in the future.
|
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
ghcup has increasingly become difficult to maintain. A few reasons:
|
Maintenance problems:
|
||||||
|
|
||||||
* few maintainers
|
|
||||||
* increasing LOC
|
|
||||||
* platform incompatibilities regularly causing breaking bugs:
|
* platform incompatibilities regularly causing breaking bugs:
|
||||||
* [Mktemp not working properly on macOS](https://gitlab.haskell.org/haskell/ghcup/issues/130)
|
* [Mktemp not working properly on macOS](https://gitlab.haskell.org/haskell/ghcup/issues/130)
|
||||||
* [ln: illegal option -- T on macOS Catalina](https://gitlab.haskell.org/haskell/ghcup/issues/123)
|
* [ln: illegal option -- T on macOS Catalina](https://gitlab.haskell.org/haskell/ghcup/issues/123)
|
||||||
* [Wrong tar flag on darwin](https://gitlab.haskell.org/haskell/ghcup/issues/119))
|
* [Wrong tar flag on darwin](https://gitlab.haskell.org/haskell/ghcup/issues/119))
|
||||||
* refactoring being difficult due to POSIX sh
|
* refactoring being difficult due to POSIX sh
|
||||||
|
|
||||||
More benefits of a rewrite:
|
Benefits of a rewrite:
|
||||||
|
|
||||||
* Features such as installing [release candidates](https://gitlab.haskell.org/haskell/ghcup/issues/94) or [HEAD builds](https://gitlab.haskell.org/haskell/ghcup/issues/65) can be more conveniently implemented in a rewrite
|
* Features such as installing [release candidates](https://gitlab.haskell.org/haskell/ghcup/issues/94) or [HEAD builds](https://gitlab.haskell.org/haskell/ghcup/issues/65) can be more conveniently implemented in a rewrite
|
||||||
* Refactoring will be easier
|
* Refactoring will be easier
|
||||||
* Better tool support (such as linting the downloads file)
|
* Better tool support (such as linting the downloads file)
|
||||||
* saner downloads file format (such as JSON)
|
* saner downloads file format (such as JSON)
|
||||||
|
|
||||||
However, the downside will be:
|
Downsides:
|
||||||
|
|
||||||
* building static binaries for all platforms (and possibly causing SSL/DNS problems)
|
* building static binaries for all platforms (and possibly causing SSL/DNS problems)
|
||||||
* still bootstrapping those binaries via a POSIX sh script
|
* still bootstrapping those binaries via a POSIX sh script
|
||||||
@@ -31,4 +28,4 @@ However, the downside will be:
|
|||||||
|
|
||||||
* Correct low-level code
|
* Correct low-level code
|
||||||
* Good exception handling
|
* Good exception handling
|
||||||
* Easier user interface (possibly interactive and non-interactive ones)
|
* Cleaner user interface
|
||||||
|
|||||||
24
TODO.md
24
TODO.md
@@ -2,38 +2,40 @@
|
|||||||
|
|
||||||
## Now
|
## Now
|
||||||
|
|
||||||
* static builds and host ghcup (and fix BinaryDownloads)
|
* print-system-reqs
|
||||||
* interoperability with old ghcup
|
|
||||||
|
|
||||||
* sign the JSON? (Or check gpg keys?)
|
## Cleanups
|
||||||
|
|
||||||
|
* avoid alternative for IO
|
||||||
|
* don't use Excepts?
|
||||||
|
|
||||||
## Maybe
|
## Maybe
|
||||||
|
|
||||||
* maybe: download progress
|
|
||||||
* maybe: changelog Show the changelog of a GHC release (online)
|
* maybe: changelog Show the changelog of a GHC release (online)
|
||||||
* maybe: print-system-reqs Print an approximation of system requirements
|
|
||||||
* OS faking
|
* OS faking
|
||||||
|
* sign the JSON? (Or check gpg keys?)
|
||||||
|
|
||||||
* testing (especially distro detection -> unit tests)
|
* testing (especially distro detection -> unit tests)
|
||||||
|
|
||||||
|
* hard cleanup command?
|
||||||
|
|
||||||
## Later
|
## Later
|
||||||
|
|
||||||
|
* static builds and host ghcup
|
||||||
|
* do bootstrap-haskell with new ghcup
|
||||||
* add support for RC/alpha/HEAD versions
|
* add support for RC/alpha/HEAD versions
|
||||||
* check for updates on start
|
* check for updates on start
|
||||||
* use plucky or oops instead of Excepts
|
* use plucky or oops instead of Excepts
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
* how to figure out tools (currently not done, but when setting ghc symlinks, removes all previous tools before symlinking requested version to avoid stale tools that only exist for one version)
|
|
||||||
* handling of SIGTERM and SIGUSR
|
* handling of SIGTERM and SIGUSR
|
||||||
* installing musl on demand?
|
* installing musl on demand?
|
||||||
* redo/rethink how tool tags works
|
* redo/rethink how tool tags works
|
||||||
|
* tarball tags as well as version tags?
|
||||||
* mirror support
|
* mirror support
|
||||||
* check for new version on start
|
* check for new version on start
|
||||||
* tarball tags as well as version tags?
|
* how to propagate updates? Automatically? Might solve the versioning problem
|
||||||
* installing multiple versions in parallel?
|
* maybe add deprecation notice into JSON
|
||||||
* how to version and extend the format of the downloads file? Compatibility?
|
|
||||||
* how to propagate updates? Automatically? Might solve the versioning problem
|
|
||||||
* interactive handling when distro doesn't exist and we know the tarball is incompatible?
|
* interactive handling when distro doesn't exist and we know the tarball is incompatible?
|
||||||
* ghcup-with wrapper to execute a command with a given ghc in PATH?
|
* ghcup-with wrapper to execute a command with a given ghc in PATH?
|
||||||
* maybe add deprecation notice into JSON
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
|
|
||||||
module SourceDownloads where
|
|
||||||
|
|
||||||
|
|
||||||
import GHCup.Types
|
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
import GHCup.Utils.Version.QQ
|
|
||||||
|
|
||||||
import HPath
|
|
||||||
import URI.ByteString.QQ
|
|
||||||
|
|
||||||
import qualified Data.Map as M
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: source tarballs
|
|
||||||
-- TODO: reference tarballs
|
|
||||||
sourceDownloads :: SourceDownloads
|
|
||||||
sourceDownloads = M.fromList
|
|
||||||
[ ( [vver|8.6.5|]
|
|
||||||
, DownloadInfo
|
|
||||||
[uri|https://downloads.haskell.org/~ghc/8.6.5/ghc-8.6.5-src.tar.xz|]
|
|
||||||
(Just ([rel|ghc-8.6.5|] :: Path Rel))
|
|
||||||
[s|b47726aaf302eb87b4970fcee924d45d|]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
@@ -7,7 +7,6 @@ module Validate where
|
|||||||
import GHCup
|
import GHCup
|
||||||
import GHCup.Download
|
import GHCup.Download
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Types.Optics
|
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
|
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -48,22 +47,22 @@ addError = do
|
|||||||
validate :: (Monad m, MonadLogger m, MonadThrow m, MonadIO m, MonadUnliftIO m)
|
validate :: (Monad m, MonadLogger m, MonadThrow m, MonadIO m, MonadUnliftIO m)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> m ExitCode
|
-> m ExitCode
|
||||||
validate dls@GHCupDownloads {..} = do
|
validate dls = do
|
||||||
ref <- liftIO $ newIORef 0
|
ref <- liftIO $ newIORef 0
|
||||||
|
|
||||||
-- * verify binary downloads * --
|
-- * verify binary downloads * --
|
||||||
flip runReaderT ref $ do
|
flip runReaderT ref $ do
|
||||||
-- unique tags
|
-- unique tags
|
||||||
forM_ (M.toList _binaryDownloads) $ \(t, _) -> checkUniqueTags t
|
forM_ (M.toList dls) $ \(t, _) -> checkUniqueTags t
|
||||||
|
|
||||||
-- required platforms
|
-- required platforms
|
||||||
forM_ (M.toList _binaryDownloads) $ \(t, versions) ->
|
forM_ (M.toList dls) $ \(t, versions) ->
|
||||||
forM_ (M.toList versions) $ \(v, vi) ->
|
forM_ (M.toList versions) $ \(v, vi) ->
|
||||||
forM_ (M.toList $ _viArch vi) $ \(arch, pspecs) -> do
|
forM_ (M.toList $ _viArch vi) $ \(arch, pspecs) -> do
|
||||||
checkHasRequiredPlatforms t v arch (M.keys pspecs)
|
checkHasRequiredPlatforms t v arch (M.keys pspecs)
|
||||||
|
|
||||||
checkGHCisSemver
|
checkGHCisSemver
|
||||||
forM_ (M.toList _binaryDownloads) $ \(t, _) -> checkMandatoryTags t
|
forM_ (M.toList dls) $ \(t, _) -> checkMandatoryTags t
|
||||||
|
|
||||||
-- exit
|
-- exit
|
||||||
e <- liftIO $ readIORef ref
|
e <- liftIO $ readIORef ref
|
||||||
@@ -86,7 +85,7 @@ validate dls@GHCupDownloads {..} = do
|
|||||||
[i|FreeBSD missing for #{t} #{v'} #{arch}|]
|
[i|FreeBSD missing for #{t} #{v'} #{arch}|]
|
||||||
|
|
||||||
checkUniqueTags tool = do
|
checkUniqueTags tool = do
|
||||||
let allTags = join $ fmap snd $ availableToolVersions _binaryDownloads tool
|
let allTags = join $ fmap snd $ availableToolVersions dls tool
|
||||||
let nonUnique =
|
let nonUnique =
|
||||||
fmap fst
|
fmap fst
|
||||||
. filter (\(_, b) -> not b)
|
. filter (\(_, b) -> not b)
|
||||||
@@ -110,7 +109,7 @@ validate dls@GHCupDownloads {..} = do
|
|||||||
isUniqueTag Recommended = True
|
isUniqueTag Recommended = True
|
||||||
|
|
||||||
checkGHCisSemver = do
|
checkGHCisSemver = do
|
||||||
let ghcVers = toListOf (binaryDownloads % ix GHC % to M.keys % folded) dls
|
let ghcVers = toListOf (ix GHC % to M.keys % folded) dls
|
||||||
forM_ ghcVers $ \v -> case semver (prettyVer v) of
|
forM_ ghcVers $ \v -> case semver (prettyVer v) of
|
||||||
Left _ -> do
|
Left _ -> do
|
||||||
lift $ $(logError) [i|GHC version #{v} is not valid semver|]
|
lift $ $(logError) [i|GHC version #{v} is not valid semver|]
|
||||||
@@ -119,7 +118,7 @@ validate dls@GHCupDownloads {..} = do
|
|||||||
|
|
||||||
-- a tool must have at least one of each mandatory tags
|
-- a tool must have at least one of each mandatory tags
|
||||||
checkMandatoryTags tool = do
|
checkMandatoryTags tool = do
|
||||||
let allTags = join $ fmap snd $ availableToolVersions _binaryDownloads tool
|
let allTags = join $ fmap snd $ availableToolVersions dls tool
|
||||||
forM_ [Latest, Recommended] $ \t -> case elem t allTags of
|
forM_ [Latest, Recommended] $ \t -> case elem t allTags of
|
||||||
False -> do
|
False -> do
|
||||||
lift $ $(logError) [i|Tag #{t} missing from #{tool}|]
|
lift $ $(logError) [i|Tag #{t} missing from #{tool}|]
|
||||||
@@ -132,20 +131,25 @@ validateTarballs :: ( Monad m
|
|||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadUnliftIO m
|
, MonadUnliftIO m
|
||||||
|
, MonadMask m
|
||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> m ExitCode
|
-> m ExitCode
|
||||||
validateTarballs GHCupDownloads {..} = do
|
validateTarballs dls = do
|
||||||
ref <- liftIO $ newIORef 0
|
ref <- liftIO $ newIORef 0
|
||||||
|
|
||||||
flip runReaderT ref $ do
|
flip runReaderT ref $ do
|
||||||
-- download/verify all tarballs
|
-- download/verify all binary tarballs
|
||||||
let
|
let
|
||||||
dlis = nub $ join $ (M.elems _binaryDownloads) <&> \versions ->
|
dlbis = nub $ join $ (M.elems dls) <&> \versions ->
|
||||||
join $ (M.elems versions) <&> \vi ->
|
join $ (M.elems versions) <&> \vi ->
|
||||||
join $ (M.elems $ _viArch vi) <&> \pspecs ->
|
join $ (M.elems $ _viArch vi) <&> \pspecs ->
|
||||||
join $ (M.elems pspecs) <&> \pverspecs -> (M.elems pverspecs)
|
join $ (M.elems pspecs) <&> \pverspecs -> (M.elems pverspecs)
|
||||||
forM_ dlis $ downloadAll
|
forM_ dlbis $ downloadAll
|
||||||
|
|
||||||
|
let dlsrc = nub $ join $ (M.elems dls) <&> \versions ->
|
||||||
|
join $ (M.elems versions) <&> maybe [] (: []) . _viSourceDL
|
||||||
|
forM_ dlsrc $ downloadAll
|
||||||
|
|
||||||
-- exit
|
-- exit
|
||||||
e <- liftIO $ readIORef ref
|
e <- liftIO $ readIORef ref
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import GHCup.Download
|
|||||||
import GHCup.Errors
|
import GHCup.Errors
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
import GHCup.Utils.File
|
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
import GHCup.Utils.String.QQ
|
||||||
|
import GHCup.Version
|
||||||
|
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
@@ -43,6 +43,7 @@ import URI.ByteString
|
|||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.ByteString.UTF8 as UTF8
|
import qualified Data.ByteString.UTF8 as UTF8
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.IO as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
|
|
||||||
|
|
||||||
@@ -66,8 +67,9 @@ data Command
|
|||||||
| List ListOptions
|
| List ListOptions
|
||||||
| Rm RmOptions
|
| Rm RmOptions
|
||||||
| DInfo
|
| DInfo
|
||||||
| Compile CompileOptions
|
| Compile CompileCommand
|
||||||
| Upgrade UpgradeOpts
|
| Upgrade UpgradeOpts
|
||||||
|
| NumericVersion
|
||||||
|
|
||||||
data ToolVersion = ToolVersion Version
|
data ToolVersion = ToolVersion Version
|
||||||
| ToolTag Tag
|
| ToolTag Tag
|
||||||
@@ -94,8 +96,12 @@ data RmOptions = RmOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data CompileCommand = CompileGHC CompileOptions
|
||||||
|
| CompileCabal CompileOptions
|
||||||
|
|
||||||
|
|
||||||
data CompileOptions = CompileOptions
|
data CompileOptions = CompileOptions
|
||||||
{ ghcVer :: Version
|
{ targetVer :: Version
|
||||||
, bootstrapVer :: Version
|
, bootstrapVer :: Version
|
||||||
, jobs :: Maybe Int
|
, jobs :: Maybe Int
|
||||||
, buildConfig :: Maybe (Path Abs)
|
, buildConfig :: Maybe (Path Abs)
|
||||||
@@ -122,21 +128,20 @@ opts =
|
|||||||
(option
|
(option
|
||||||
(eitherReader parseUri)
|
(eitherReader parseUri)
|
||||||
(short 's' <> long "url-source" <> metavar "URL" <> help
|
(short 's' <> long "url-source" <> metavar "URL" <> help
|
||||||
"Alternative ghcup download info url"
|
"Alternative ghcup download info url" <> internal
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<*> switch
|
<*> switch
|
||||||
( short 'n'
|
(short 'n' <> long "no-verify" <> help
|
||||||
<> long "no-verify"
|
"Skip tarball checksum verification (default: False)"
|
||||||
<> help
|
|
||||||
"Skip tarball checksum checks (default: False)"
|
|
||||||
)
|
)
|
||||||
<*> com
|
<*> com
|
||||||
where
|
where
|
||||||
parseUri s' =
|
parseUri s' =
|
||||||
bimap show id $ parseURI strictURIParserOptions (UTF8.fromString s')
|
bimap show id $ parseURI strictURIParserOptions (UTF8.fromString s')
|
||||||
|
|
||||||
|
|
||||||
com :: Parser Command
|
com :: Parser Command
|
||||||
com =
|
com =
|
||||||
subparser
|
subparser
|
||||||
@@ -162,6 +167,13 @@ com =
|
|||||||
(progDesc "Upgrade ghcup (per default in ~/.ghcup/bin/)")
|
(progDesc "Upgrade ghcup (per default in ~/.ghcup/bin/)")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
<> command
|
||||||
|
"compile"
|
||||||
|
( Compile
|
||||||
|
<$> (info (compileP <**> helper)
|
||||||
|
(progDesc "Compile a tool from source")
|
||||||
|
)
|
||||||
|
)
|
||||||
<> commandGroup "Main commands:"
|
<> commandGroup "Main commands:"
|
||||||
)
|
)
|
||||||
<|> subparser
|
<|> subparser
|
||||||
@@ -180,13 +192,6 @@ com =
|
|||||||
(progDesc "Remove a GHC version installed by ghcup")
|
(progDesc "Remove a GHC version installed by ghcup")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<> command
|
|
||||||
"compile"
|
|
||||||
( Compile
|
|
||||||
<$> (info (compileOpts <**> helper)
|
|
||||||
(progDesc "Compile GHC from source")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
<> commandGroup "GHC commands:"
|
<> commandGroup "GHC commands:"
|
||||||
<> hidden
|
<> hidden
|
||||||
)
|
)
|
||||||
@@ -194,6 +199,11 @@ com =
|
|||||||
( command
|
( command
|
||||||
"debug-info"
|
"debug-info"
|
||||||
((\_ -> DInfo) <$> (info (helper) (progDesc "Show debug info")))
|
((\_ -> DInfo) <$> (info (helper) (progDesc "Show debug info")))
|
||||||
|
<> command
|
||||||
|
"numeric-version"
|
||||||
|
( (\_ -> NumericVersion)
|
||||||
|
<$> (info (helper) (progDesc "Show the numeric version"))
|
||||||
|
)
|
||||||
<> commandGroup "Other commands:"
|
<> commandGroup "Other commands:"
|
||||||
<> hidden
|
<> hidden
|
||||||
)
|
)
|
||||||
@@ -246,6 +256,24 @@ rmOpts :: Parser RmOptions
|
|||||||
rmOpts = RmOptions <$> versionParser
|
rmOpts = RmOptions <$> versionParser
|
||||||
|
|
||||||
|
|
||||||
|
compileP :: Parser CompileCommand
|
||||||
|
compileP = subparser
|
||||||
|
( command
|
||||||
|
"ghc"
|
||||||
|
( CompileGHC
|
||||||
|
<$> (info (compileOpts <**> helper) (progDesc "Compile GHC from source")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
<> command
|
||||||
|
"cabal"
|
||||||
|
( CompileCabal
|
||||||
|
<$> (info (compileOpts <**> helper)
|
||||||
|
(progDesc "Compile Cabal from source")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
compileOpts :: Parser CompileOptions
|
compileOpts :: Parser CompileOptions
|
||||||
compileOpts =
|
compileOpts =
|
||||||
CompileOptions
|
CompileOptions
|
||||||
@@ -254,7 +282,7 @@ compileOpts =
|
|||||||
(bimap (const "Not a valid version") id . version . T.pack)
|
(bimap (const "Not a valid version") id . version . T.pack)
|
||||||
)
|
)
|
||||||
(short 'v' <> long "version" <> metavar "VERSION" <> help
|
(short 'v' <> long "version" <> metavar "VERSION" <> help
|
||||||
"The GHC version to compile"
|
"The tool version to compile"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<*> (option
|
<*> (option
|
||||||
@@ -362,8 +390,6 @@ upgradeOptsP =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: something better than Show instance for errors
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
|
|
||||||
@@ -386,19 +412,19 @@ main = do
|
|||||||
. runResourceT
|
. runResourceT
|
||||||
. runE
|
. runE
|
||||||
@'[ AlreadyInstalled
|
@'[ AlreadyInstalled
|
||||||
, ArchiveError
|
, UnknownArchive
|
||||||
, DistroNotFound
|
, DistroNotFound
|
||||||
, FileDoesNotExistError
|
, FileDoesNotExistError
|
||||||
, FileError
|
, CopyError
|
||||||
, JSONError
|
, JSONError
|
||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, NotInstalled
|
, NotInstalled
|
||||||
, PlatformResultError
|
, NoCompatiblePlatform
|
||||||
, ProcessError
|
, BuildFailed
|
||||||
, TagNotFound
|
, TagNotFound
|
||||||
, URLException
|
|
||||||
, DigestError
|
, DigestError
|
||||||
|
, DownloadFailed
|
||||||
]
|
]
|
||||||
|
|
||||||
let runSetGHC =
|
let runSetGHC =
|
||||||
@@ -408,15 +434,15 @@ main = do
|
|||||||
@'[ FileDoesNotExistError
|
@'[ FileDoesNotExistError
|
||||||
, NotInstalled
|
, NotInstalled
|
||||||
, TagNotFound
|
, TagNotFound
|
||||||
, URLException
|
|
||||||
, JSONError
|
, JSONError
|
||||||
, TagNotFound
|
, TagNotFound
|
||||||
|
, DownloadFailed
|
||||||
]
|
]
|
||||||
|
|
||||||
let runListGHC =
|
let runListGHC =
|
||||||
runLogger
|
runLogger
|
||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runE @'[FileDoesNotExistError , URLException , JSONError]
|
. runE @'[FileDoesNotExistError , JSONError , DownloadFailed]
|
||||||
|
|
||||||
let runRmGHC =
|
let runRmGHC =
|
||||||
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
||||||
@@ -425,7 +451,7 @@ main = do
|
|||||||
runLogger
|
runLogger
|
||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runE
|
. runE
|
||||||
@'[PlatformResultError , NoCompatibleArch , DistroNotFound]
|
@'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
||||||
|
|
||||||
let runCompileGHC =
|
let runCompileGHC =
|
||||||
runLogger
|
runLogger
|
||||||
@@ -433,31 +459,43 @@ main = do
|
|||||||
. runResourceT
|
. runResourceT
|
||||||
. runE
|
. runE
|
||||||
@'[ AlreadyInstalled
|
@'[ AlreadyInstalled
|
||||||
, NotInstalled
|
, BuildFailed
|
||||||
, GHCNotFound
|
|
||||||
, ArchiveError
|
|
||||||
, ProcessError
|
|
||||||
, URLException
|
|
||||||
, DigestError
|
, DigestError
|
||||||
, BuildConfigNotFound
|
, DownloadFailed
|
||||||
, FileDoesNotExistError
|
, GHCupSetError
|
||||||
, URLException
|
, NoDownload
|
||||||
|
, UnknownArchive
|
||||||
|
--
|
||||||
, JSONError
|
, JSONError
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let runCompileCabal =
|
||||||
|
runLogger
|
||||||
|
. flip runReaderT settings
|
||||||
|
. runResourceT
|
||||||
|
. runE
|
||||||
|
@'[ JSONError
|
||||||
|
, UnknownArchive
|
||||||
|
, NoDownload
|
||||||
|
, DigestError
|
||||||
|
, DownloadFailed
|
||||||
|
, BuildFailed
|
||||||
|
]
|
||||||
|
|
||||||
let runUpgrade =
|
let runUpgrade =
|
||||||
runLogger
|
runLogger
|
||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runResourceT
|
. runResourceT
|
||||||
. runE
|
. runE
|
||||||
@'[ DigestError
|
@'[ DigestError
|
||||||
, URLException
|
|
||||||
, DistroNotFound
|
, DistroNotFound
|
||||||
, PlatformResultError
|
, NoCompatiblePlatform
|
||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, FileDoesNotExistError
|
, FileDoesNotExistError
|
||||||
, JSONError
|
, JSONError
|
||||||
|
, DownloadFailed
|
||||||
|
, CopyError
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -465,16 +503,22 @@ main = do
|
|||||||
Install (InstallGHC InstallOptions {..}) ->
|
Install (InstallGHC InstallOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runInstTool $ do
|
$ (runInstTool $ do
|
||||||
dls <- _binaryDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
v <- liftE $ fromVersion dls instVer GHC
|
v <- liftE $ fromVersion dls instVer GHC
|
||||||
liftE $ installTool dls (ToolRequest GHC v) Nothing
|
liftE $ installGHCBin dls v Nothing
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> runLogger
|
VRight _ -> runLogger
|
||||||
$ $(logInfo) ([s|GHC installation successful|])
|
$ $(logInfo) ([s|GHC installation successful|])
|
||||||
VLeft (V (AlreadyInstalled treq)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
(T.pack (show treq) <> [s| already installed|])
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
|
runLogger
|
||||||
|
($(logError) [i|Build failed with #{e}
|
||||||
|
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
|
)
|
||||||
|
>> exitFailure
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger $ do
|
runLogger $ do
|
||||||
$(logError) [i|#{e}|]
|
$(logError) [i|#{e}|]
|
||||||
@@ -483,16 +527,16 @@ main = do
|
|||||||
Install (InstallCabal InstallOptions {..}) ->
|
Install (InstallCabal InstallOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runInstTool $ do
|
$ (runInstTool $ do
|
||||||
dls <- _binaryDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
v <- liftE $ fromVersion dls instVer Cabal
|
v <- liftE $ fromVersion dls instVer Cabal
|
||||||
liftE $ installTool dls (ToolRequest Cabal v) Nothing
|
liftE $ installCabalBin dls v Nothing
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> runLogger
|
VRight _ -> runLogger
|
||||||
$ $(logInfo) ([s|Cabal installation successful|])
|
$ $(logInfo) ([s|Cabal installation successful|])
|
||||||
VLeft (V (AlreadyInstalled treq)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
(T.pack (show treq) <> [s| already installed|])
|
[i|Cabal ver #{prettyVer v} already installed|]
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger $ do
|
runLogger $ do
|
||||||
$(logError) [i|#{e}|]
|
$(logError) [i|#{e}|]
|
||||||
@@ -502,7 +546,7 @@ main = do
|
|||||||
SetGHC (SetGHCOptions {..}) ->
|
SetGHC (SetGHCOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runSetGHC $ do
|
$ (runSetGHC $ do
|
||||||
dls <- _binaryDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
v <- liftE $ fromVersion dls ghcVer GHC
|
v <- liftE $ fromVersion dls ghcVer GHC
|
||||||
liftE $ setGHC v SetGHCOnly
|
liftE $ setGHC v SetGHCOnly
|
||||||
)
|
)
|
||||||
@@ -515,7 +559,7 @@ main = do
|
|||||||
List (ListOptions {..}) ->
|
List (ListOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runListGHC $ do
|
$ (runListGHC $ do
|
||||||
dls <- _binaryDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
liftIO $ listVersions dls lTool lCriteria
|
liftIO $ listVersions dls lTool lCriteria
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
@@ -543,24 +587,52 @@ main = do
|
|||||||
VLeft e ->
|
VLeft e ->
|
||||||
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
||||||
|
|
||||||
Compile (CompileOptions {..}) ->
|
Compile (CompileGHC CompileOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runCompileGHC $ do
|
$ (runCompileGHC $ do
|
||||||
dls <- _sourceDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
liftE $ compileGHC dls ghcVer bootstrapVer jobs buildConfig
|
liftE
|
||||||
|
$ compileGHC dls targetVer bootstrapVer jobs buildConfig
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ ->
|
VRight _ ->
|
||||||
runLogger $ $(logInfo)
|
runLogger $ $(logInfo)
|
||||||
([s|GHC successfully compiled and installed|])
|
([s|GHC successfully compiled and installed|])
|
||||||
VLeft (V (AlreadyInstalled treq)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
(T.pack (show treq) <> [s| already installed|])
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
|
runLogger
|
||||||
|
($(logError) [i|Build failed with #{e}
|
||||||
|
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
|
)
|
||||||
|
>> exitFailure
|
||||||
|
VLeft e ->
|
||||||
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
||||||
|
|
||||||
|
Compile (CompileCabal CompileOptions {..}) ->
|
||||||
|
void
|
||||||
|
$ (runCompileCabal $ do
|
||||||
|
dls <- liftE getDownloads
|
||||||
|
liftE $ compileCabal dls
|
||||||
|
targetVer
|
||||||
|
bootstrapVer
|
||||||
|
jobs
|
||||||
|
)
|
||||||
|
>>= \case
|
||||||
|
VRight _ ->
|
||||||
|
runLogger $ $(logInfo)
|
||||||
|
([s|Cabal successfully compiled and installed|])
|
||||||
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
|
runLogger
|
||||||
|
($(logError) [i|Build failed with #{e}
|
||||||
|
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
|
)
|
||||||
|
>> exitFailure
|
||||||
VLeft e ->
|
VLeft e ->
|
||||||
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
||||||
|
|
||||||
Upgrade (uOpts) -> do
|
Upgrade (uOpts) -> do
|
||||||
liftIO $ putStrLn $ show uOpts
|
|
||||||
target <- case uOpts of
|
target <- case uOpts of
|
||||||
UpgradeInplace -> do
|
UpgradeInplace -> do
|
||||||
efp <- liftIO $ getExecutablePath
|
efp <- liftIO $ getExecutablePath
|
||||||
@@ -573,7 +645,7 @@ main = do
|
|||||||
|
|
||||||
void
|
void
|
||||||
$ (runUpgrade $ do
|
$ (runUpgrade $ do
|
||||||
dls <- _binaryDownloads <$> liftE getDownloads
|
dls <- liftE getDownloads
|
||||||
liftE $ upgradeGHCup dls target
|
liftE $ upgradeGHCup dls target
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
@@ -585,11 +657,12 @@ main = do
|
|||||||
VLeft e ->
|
VLeft e ->
|
||||||
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
||||||
|
|
||||||
|
NumericVersion -> T.hPutStr stdout (prettyPVP ghcUpVer)
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
|
||||||
fromVersion :: Monad m
|
fromVersion :: Monad m
|
||||||
=> BinaryDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe ToolVersion
|
-> Maybe ToolVersion
|
||||||
-> Tool
|
-> Tool
|
||||||
-> Excepts '[TagNotFound] m Version
|
-> Excepts '[TagNotFound] m Version
|
||||||
@@ -611,6 +684,7 @@ printListResult lr = do
|
|||||||
, column expand left def def
|
, column expand left def def
|
||||||
, column expand left def def
|
, column expand left def def
|
||||||
, column expand left def def
|
, column expand left def def
|
||||||
|
, column expand left def def
|
||||||
]
|
]
|
||||||
$ fmap
|
$ fmap
|
||||||
(\ListResult {..} ->
|
(\ListResult {..} ->
|
||||||
@@ -621,6 +695,7 @@ printListResult lr = do
|
|||||||
, fmap toLower . show $ lTool
|
, fmap toLower . show $ lTool
|
||||||
, T.unpack . prettyVer $ lVer
|
, T.unpack . prettyVer $ lVer
|
||||||
, intercalate "," $ ((fmap . fmap) toLower . fmap show $ lTag)
|
, intercalate "," $ ((fmap . fmap) toLower . fmap show $ lTag)
|
||||||
|
, if fromSrc then (color Blue "compiled") else mempty
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
lr
|
lr
|
||||||
|
|||||||
@@ -13,12 +13,3 @@ package ghcup
|
|||||||
package tar-bytestring
|
package tar-bytestring
|
||||||
ghc-options: -O2
|
ghc-options: -O2
|
||||||
|
|
||||||
source-repository-package
|
|
||||||
type: git
|
|
||||||
location: https://github.com/composewell/streamly
|
|
||||||
tag: 4eb53e7f868bdc08afcc4b5210ab5916b9a4dfbc
|
|
||||||
|
|
||||||
source-repository-package
|
|
||||||
type: git
|
|
||||||
location: https://github.com/hasufell/tar-bytestring
|
|
||||||
tag: 64707be1abb534e88007e3320090598a0a9490a7
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
constraints: any.Cabal ==2.4.0.1,
|
constraints: any.Cabal ==2.4.0.1,
|
||||||
any.HUnit ==1.6.0.0,
|
|
||||||
any.HsOpenSSL ==0.11.4.17,
|
any.HsOpenSSL ==0.11.4.17,
|
||||||
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -old-locale,
|
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -old-locale,
|
||||||
any.IfElse ==0.85,
|
any.IfElse ==0.85,
|
||||||
@@ -10,81 +9,97 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
abstract-deque -usecas,
|
abstract-deque -usecas,
|
||||||
any.aeson ==1.4.6.0,
|
any.aeson ==1.4.6.0,
|
||||||
aeson -bytestring-builder -cffi -developer -fast,
|
aeson -bytestring-builder -cffi -developer -fast,
|
||||||
|
any.aeson-pretty ==0.8.8,
|
||||||
|
aeson-pretty -lib-only,
|
||||||
any.ansi-terminal ==0.10.3,
|
any.ansi-terminal ==0.10.3,
|
||||||
ansi-terminal -example,
|
ansi-terminal -example,
|
||||||
|
any.ansi-wl-pprint ==0.6.9,
|
||||||
|
ansi-wl-pprint -example,
|
||||||
any.array ==0.5.3.0,
|
any.array ==0.5.3.0,
|
||||||
any.ascii-string ==1.0.1.4,
|
any.ascii-string ==1.0.1.4,
|
||||||
|
any.assoc ==1.0.1,
|
||||||
any.async ==2.2.2,
|
any.async ==2.2.2,
|
||||||
async -bench,
|
async -bench,
|
||||||
any.atomic-primops ==0.8.3,
|
any.atomic-primops ==0.8.3,
|
||||||
atomic-primops -debug,
|
atomic-primops -debug,
|
||||||
any.attoparsec ==0.13.2.3,
|
any.attoparsec ==0.13.2.3,
|
||||||
attoparsec -developer,
|
attoparsec -developer,
|
||||||
|
any.auto-update ==0.1.6,
|
||||||
any.base ==4.12.0.0,
|
any.base ==4.12.0.0,
|
||||||
any.base-compat ==0.11.1,
|
any.base-compat ==0.11.1,
|
||||||
any.base-orphans ==0.8.2,
|
any.base-orphans ==0.8.2,
|
||||||
any.base-prelude ==1.3,
|
any.base-prelude ==1.3,
|
||||||
|
any.base16-bytestring ==0.1.1.6,
|
||||||
any.base64-bytestring ==1.0.0.3,
|
any.base64-bytestring ==1.0.0.3,
|
||||||
any.basement ==0.0.11,
|
|
||||||
any.bifunctors ==5.5.7,
|
any.bifunctors ==5.5.7,
|
||||||
bifunctors +semigroups +tagged,
|
bifunctors +semigroups +tagged,
|
||||||
any.binary ==0.8.6.0,
|
any.binary ==0.8.6.0,
|
||||||
any.blaze-builder ==0.4.1.0,
|
any.blaze-builder ==0.4.1.0,
|
||||||
|
any.brotli ==0.0.0.0,
|
||||||
|
any.brotli-streams ==0.0.0.0,
|
||||||
any.bytestring ==0.10.8.2,
|
any.bytestring ==0.10.8.2,
|
||||||
any.bytestring-builder ==0.10.8.2.0,
|
any.bytestring-builder ==0.10.8.2.0,
|
||||||
bytestring-builder +bytestring_has_builder,
|
bytestring-builder +bytestring_has_builder,
|
||||||
any.bzlib ==0.5.0.5,
|
any.bzlib ==0.5.0.5,
|
||||||
any.cabal-doctest ==1.0.8,
|
any.cabal-doctest ==1.0.8,
|
||||||
any.call-stack ==0.2.0,
|
|
||||||
any.case-insensitive ==1.2.1.0,
|
any.case-insensitive ==1.2.1.0,
|
||||||
any.cereal ==0.5.8.1,
|
any.cereal ==0.5.8.1,
|
||||||
cereal -bytestring-builder,
|
cereal -bytestring-builder,
|
||||||
any.clock ==0.8,
|
any.clock ==0.8,
|
||||||
clock -llvm,
|
clock -llvm,
|
||||||
|
any.cmdargs ==0.10.20,
|
||||||
|
cmdargs +quotation -testprog,
|
||||||
any.colour ==2.3.5,
|
any.colour ==2.3.5,
|
||||||
any.comonad ==5.0.6,
|
any.comonad ==5.0.6,
|
||||||
comonad +containers +distributive +test-doctests,
|
comonad +containers +distributive +test-doctests,
|
||||||
|
any.conduit ==1.3.1.2,
|
||||||
|
any.conduit-extra ==1.3.4,
|
||||||
any.containers ==0.6.0.1,
|
any.containers ==0.6.0.1,
|
||||||
any.contravariant ==1.5.2,
|
any.contravariant ==1.5.2,
|
||||||
contravariant +semigroups +statevar +tagged,
|
contravariant +semigroups +statevar +tagged,
|
||||||
any.data-default-class ==0.1.2.0,
|
any.data-default-class ==0.1.2.0,
|
||||||
|
any.data-default-instances-base ==0.1.0.1,
|
||||||
any.deepseq ==1.4.4.0,
|
any.deepseq ==1.4.4.0,
|
||||||
any.deferred-folds ==0.9.10.1,
|
any.deferred-folds ==0.9.10.1,
|
||||||
any.directory ==1.3.3.0,
|
any.directory ==1.3.3.0 || ==1.3.6.0,
|
||||||
any.distributive ==0.6.1,
|
any.distributive ==0.6.1,
|
||||||
distributive +semigroups +tagged,
|
distributive +semigroups +tagged,
|
||||||
any.dlist ==0.8.0.7,
|
any.dlist ==0.8.0.7,
|
||||||
|
any.easy-file ==0.2.2,
|
||||||
|
any.errors ==2.3.0,
|
||||||
any.exceptions ==0.10.4,
|
any.exceptions ==0.10.4,
|
||||||
exceptions +transformers-0-4,
|
exceptions +transformers-0-4,
|
||||||
|
any.extra ==1.7,
|
||||||
|
any.fast-logger ==3.0.1,
|
||||||
any.filepath ==1.4.2.1,
|
any.filepath ==1.4.2.1,
|
||||||
any.focus ==1.0.1.3,
|
any.focus ==1.0.1.3,
|
||||||
any.foldl ==1.4.6,
|
any.foldl ==1.4.6,
|
||||||
any.fusion-plugin ==0.1.1,
|
any.free ==5.1.3,
|
||||||
any.gauge ==0.2.5,
|
any.fusion-plugin-types ==0.1.0,
|
||||||
gauge +analysis,
|
|
||||||
any.generics-sop ==0.5.0.0,
|
any.generics-sop ==0.5.0.0,
|
||||||
any.ghc ==8.6.5,
|
|
||||||
any.ghc-boot ==8.6.5,
|
|
||||||
any.ghc-boot-th ==8.6.5,
|
any.ghc-boot-th ==8.6.5,
|
||||||
any.ghc-heap ==8.6.5,
|
|
||||||
any.ghc-prim ==0.5.3,
|
any.ghc-prim ==0.5.3,
|
||||||
any.ghci ==8.6.5,
|
any.happy ==1.19.12,
|
||||||
|
happy +small_base,
|
||||||
any.hashable ==1.3.0.0,
|
any.hashable ==1.3.0.0,
|
||||||
hashable -examples +integer-gmp +sse2 -sse41,
|
hashable -examples +integer-gmp +sse2 -sse41,
|
||||||
|
any.haskell-src-exts ==1.23.0,
|
||||||
|
any.haskell-src-meta ==0.8.5,
|
||||||
|
any.haskus-utils-data ==1.2,
|
||||||
|
any.haskus-utils-types ==1.5,
|
||||||
|
any.haskus-utils-variant ==3.0,
|
||||||
any.heaps ==0.3.6.1,
|
any.heaps ==0.3.6.1,
|
||||||
|
any.hopenssl ==2.2.4,
|
||||||
|
hopenssl -link-libz,
|
||||||
any.hpath ==0.11.0,
|
any.hpath ==0.11.0,
|
||||||
any.hpath-directory ==0.13.2,
|
any.hpath-directory ==0.13.2,
|
||||||
any.hpath-filepath ==0.10.4,
|
any.hpath-filepath ==0.10.4,
|
||||||
any.hpath-io ==0.13.1,
|
any.hpath-io ==0.13.1,
|
||||||
any.hpath-posix ==0.13.1,
|
any.hpath-posix ==0.13.1,
|
||||||
any.hpc ==0.6.0.3,
|
|
||||||
any.hsc2hs ==0.68.6,
|
any.hsc2hs ==0.68.6,
|
||||||
hsc2hs -in-ghc-tree,
|
hsc2hs -in-ghc-tree,
|
||||||
any.hspec ==2.7.1,
|
any.http-io-streams ==0.1.2.0,
|
||||||
any.hspec-core ==2.7.1,
|
http-io-streams +brotli,
|
||||||
any.hspec-discover ==2.7.1,
|
|
||||||
any.hspec-expectations ==0.8.2,
|
|
||||||
any.http-io-streams ==0.1.0.0,
|
|
||||||
any.indexed-profunctors ==0.1,
|
any.indexed-profunctors ==0.1,
|
||||||
any.integer-gmp ==1.0.2.0,
|
any.integer-gmp ==1.0.2.0,
|
||||||
any.integer-logarithms ==1.0.3,
|
any.integer-logarithms ==1.0.3,
|
||||||
@@ -92,6 +107,7 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.io-streams ==1.5.1.0,
|
any.io-streams ==1.5.1.0,
|
||||||
io-streams -nointeractivetests,
|
io-streams -nointeractivetests,
|
||||||
any.language-bash ==0.9.0,
|
any.language-bash ==0.9.0,
|
||||||
|
any.lifted-base ==0.2.3.12,
|
||||||
any.list-t ==1.0.4,
|
any.list-t ==1.0.4,
|
||||||
any.lockfree-queue ==0.2.3.1,
|
any.lockfree-queue ==0.2.3.1,
|
||||||
any.lzma ==0.0.0.3,
|
any.lzma ==0.0.0.3,
|
||||||
@@ -101,29 +117,42 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
megaparsec -dev,
|
megaparsec -dev,
|
||||||
any.mmorph ==1.1.3,
|
any.mmorph ==1.1.3,
|
||||||
any.monad-control ==1.0.2.3,
|
any.monad-control ==1.0.2.3,
|
||||||
|
any.monad-logger ==0.3.32,
|
||||||
|
monad-logger +template_haskell,
|
||||||
|
any.monad-loops ==0.4.3,
|
||||||
|
monad-loops +base4,
|
||||||
|
any.mono-traversable ==1.0.15.1,
|
||||||
any.mtl ==2.2.2,
|
any.mtl ==2.2.2,
|
||||||
any.mwc-random ==0.14.0.0,
|
any.mwc-random ==0.14.0.0,
|
||||||
any.network ==3.0.1.1,
|
any.network ==3.1.1.1,
|
||||||
any.network-uri ==2.6.2.0,
|
any.network-uri ==2.6.3.0,
|
||||||
|
any.old-locale ==1.0.0.7,
|
||||||
|
any.old-time ==1.1.0.3,
|
||||||
any.openssl-streams ==1.2.2.0,
|
any.openssl-streams ==1.2.2.0,
|
||||||
any.optics ==0.2,
|
any.optics ==0.2,
|
||||||
any.optics-core ==0.2,
|
any.optics-core ==0.2,
|
||||||
any.optics-extra ==0.2,
|
any.optics-extra ==0.2,
|
||||||
any.optics-th ==0.2,
|
any.optics-th ==0.2,
|
||||||
|
any.optics-vl ==0.2,
|
||||||
|
any.optparse-applicative ==0.15.1.0,
|
||||||
any.parsec ==3.1.13.0,
|
any.parsec ==3.1.13.0,
|
||||||
any.parser-combinators ==1.2.1,
|
any.parser-combinators ==1.2.1,
|
||||||
parser-combinators -dev,
|
parser-combinators -dev,
|
||||||
any.pretty ==1.1.3.6,
|
any.pretty ==1.1.3.6,
|
||||||
|
any.pretty-terminal ==0.1.0.0,
|
||||||
any.prettyprinter ==1.6.1,
|
any.prettyprinter ==1.6.1,
|
||||||
prettyprinter -buildreadme,
|
prettyprinter -buildreadme,
|
||||||
any.primitive ==0.7.0.0,
|
any.primitive ==0.7.0.1,
|
||||||
any.primitive-extras ==0.8,
|
any.primitive-extras ==0.8,
|
||||||
any.primitive-unlifted ==0.1.3.0,
|
any.primitive-unlifted ==0.1.3.0,
|
||||||
any.process ==1.6.5.0,
|
any.process ==1.6.5.0 || ==1.6.8.0,
|
||||||
any.profunctors ==5.5.2,
|
any.profunctors ==5.5.2,
|
||||||
any.quickcheck-io ==0.2.0,
|
|
||||||
any.random ==1.1,
|
any.random ==1.1,
|
||||||
|
any.recursion-schemes ==5.1.3,
|
||||||
|
recursion-schemes +template-haskell,
|
||||||
|
any.resourcet ==1.2.3,
|
||||||
any.rts ==1.0,
|
any.rts ==1.0,
|
||||||
|
any.safe ==0.3.18,
|
||||||
any.safe-exceptions ==0.1.7.0,
|
any.safe-exceptions ==0.1.7.0,
|
||||||
any.scientific ==0.3.6.2,
|
any.scientific ==0.3.6.2,
|
||||||
scientific -bytestring-builder -integer-simple,
|
scientific -bytestring-builder -integer-simple,
|
||||||
@@ -131,27 +160,42 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
semigroupoids +comonad +containers +contravariant +distributive +doctests +tagged +unordered-containers,
|
semigroupoids +comonad +containers +contravariant +distributive +doctests +tagged +unordered-containers,
|
||||||
any.semigroups ==0.19.1,
|
any.semigroups ==0.19.1,
|
||||||
semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers,
|
semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers,
|
||||||
any.setenv ==0.1.1.3,
|
|
||||||
any.sop-core ==0.5.0.0,
|
any.sop-core ==0.5.0.0,
|
||||||
any.splitmix ==0.0.3,
|
any.split ==0.2.3.4,
|
||||||
|
any.splitmix ==0.0.4,
|
||||||
splitmix -optimised-mixer +random,
|
splitmix -optimised-mixer +random,
|
||||||
any.stm ==2.5.0.0,
|
any.stm ==2.5.0.0,
|
||||||
|
any.stm-chans ==3.0.0.4,
|
||||||
|
any.streaming-commons ==0.2.1.2,
|
||||||
|
streaming-commons -use-bytestring-builder,
|
||||||
|
any.streamly ==0.7.1,
|
||||||
streamly -debug -dev -examples -examples-sdl -fusion-plugin -has-llvm -inspection -no-charts -no-fusion -streamk,
|
streamly -debug -dev -examples -examples-sdl -fusion-plugin -has-llvm -inspection -no-charts -no-fusion -streamk,
|
||||||
any.streamly-bytestring ==0.1.2,
|
any.streamly-bytestring ==0.1.2,
|
||||||
|
any.streamly-posix ==0.1.0.0,
|
||||||
any.strict-base ==0.4.0.0,
|
any.strict-base ==0.4.0.0,
|
||||||
|
any.string-interpolate ==0.2.0.0,
|
||||||
any.syb ==0.7.1,
|
any.syb ==0.7.1,
|
||||||
|
any.table-layout ==0.8.0.5,
|
||||||
any.tagged ==0.8.6,
|
any.tagged ==0.8.6,
|
||||||
tagged +deepseq +transformers,
|
tagged +deepseq +transformers,
|
||||||
any.tar-bytestring ==0.6.2.0,
|
any.tar-bytestring ==0.6.3.0,
|
||||||
any.template-haskell ==2.14.0.0,
|
any.template-haskell ==2.14.0.0,
|
||||||
any.terminfo ==0.4.1.2,
|
any.terminal-progress-bar ==0.4.1,
|
||||||
|
any.terminal-size ==0.3.2.1,
|
||||||
any.text ==1.2.3.1,
|
any.text ==1.2.3.1,
|
||||||
|
any.text-conversions ==0.3.0,
|
||||||
any.text-icu ==0.7.0.1,
|
any.text-icu ==0.7.0.1,
|
||||||
any.text-short ==0.1.3,
|
any.text-short ==0.1.3,
|
||||||
text-short -asserts,
|
text-short -asserts,
|
||||||
any.tf-random ==0.5,
|
|
||||||
any.th-abstraction ==0.3.2.0,
|
any.th-abstraction ==0.3.2.0,
|
||||||
any.time ==1.8.0.2,
|
any.th-expand-syns ==0.4.5.0,
|
||||||
|
any.th-lift ==0.8.1,
|
||||||
|
any.th-lift-instances ==0.1.14,
|
||||||
|
any.th-orphans ==0.13.9,
|
||||||
|
any.th-reify-many ==0.1.9,
|
||||||
|
any.these ==1.0.1,
|
||||||
|
these +aeson +assoc +quickcheck +semigroupoids,
|
||||||
|
any.time ==1.8.0.2 || ==1.9.3,
|
||||||
any.time-compat ==1.9.2.2,
|
any.time-compat ==1.9.2.2,
|
||||||
time-compat -old-locale,
|
time-compat -old-locale,
|
||||||
any.transformers ==0.5.6.2,
|
any.transformers ==0.5.6.2,
|
||||||
@@ -162,14 +206,20 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.typed-process ==0.2.6.0,
|
any.typed-process ==0.2.6.0,
|
||||||
any.unix ==2.7.2.2,
|
any.unix ==2.7.2.2,
|
||||||
any.unix-bytestring ==0.3.7.3,
|
any.unix-bytestring ==0.3.7.3,
|
||||||
any.unliftio-core ==0.1.2.0,
|
any.unix-compat ==0.5.2,
|
||||||
|
unix-compat -old-time,
|
||||||
|
any.unix-time ==0.4.7,
|
||||||
|
any.unliftio-core ==0.2.0.1,
|
||||||
any.unordered-containers ==0.2.10.0,
|
any.unordered-containers ==0.2.10.0,
|
||||||
unordered-containers -debug,
|
unordered-containers -debug,
|
||||||
any.url ==2.1.3,
|
any.uri-bytestring ==0.3.2.2,
|
||||||
|
uri-bytestring -lib-werror,
|
||||||
any.utf8-string ==1.0.1.1,
|
any.utf8-string ==1.0.1.1,
|
||||||
any.uuid-types ==1.0.3,
|
any.uuid-types ==1.0.3,
|
||||||
any.vector ==0.12.1.2,
|
any.vector ==0.12.1.2,
|
||||||
vector +boundschecks -internalchecks -unsafechecks -wall,
|
vector +boundschecks -internalchecks -unsafechecks -wall,
|
||||||
|
any.vector-algorithms ==0.8.0.3,
|
||||||
|
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
|
||||||
any.vector-builder ==0.3.8,
|
any.vector-builder ==0.3.8,
|
||||||
any.vector-th-unbox ==0.2.1.7,
|
any.vector-th-unbox ==0.2.1.7,
|
||||||
any.versions ==3.5.3,
|
any.versions ==3.5.3,
|
||||||
|
|||||||
18
ghcup.cabal
18
ghcup.cabal
@@ -27,8 +27,10 @@ common ascii-string { build-depends: ascii-string >= 1.0 }
|
|||||||
common async { build-depends: async >= 0.8 }
|
common async { build-depends: async >= 0.8 }
|
||||||
common attoparsec { build-depends: attoparsec >= 0.13 }
|
common attoparsec { build-depends: attoparsec >= 0.13 }
|
||||||
common base { build-depends: base >= 4.12 && < 5 }
|
common base { build-depends: base >= 4.12 && < 5 }
|
||||||
|
common binary { build-depends: binary >= 0.8.6.0 }
|
||||||
common bytestring { build-depends: bytestring >= 0.10 }
|
common bytestring { build-depends: bytestring >= 0.10 }
|
||||||
common bzlib { build-depends: bzlib >= 0.5.0.5 }
|
common bzlib { build-depends: bzlib >= 0.5.0.5 }
|
||||||
|
common case-insensitive { build-depends: case-insensitive >= 1.2.1.0 }
|
||||||
common containers { build-depends: containers >= 0.6 }
|
common containers { build-depends: containers >= 0.6 }
|
||||||
common generics-sop { build-depends: generics-sop >= 0.5 }
|
common generics-sop { build-depends: generics-sop >= 0.5 }
|
||||||
common haskus-utils-types { build-depends: haskus-utils-types >= 1.5 }
|
common haskus-utils-types { build-depends: haskus-utils-types >= 1.5 }
|
||||||
@@ -39,7 +41,7 @@ common hpath-directory { build-depends: hpath-directory >= 0.13.2 }
|
|||||||
common hpath-filepath { build-depends: hpath-filepath >= 0.10.3 }
|
common hpath-filepath { build-depends: hpath-filepath >= 0.10.3 }
|
||||||
common hpath-io { build-depends: hpath-io >= 0.13.1 }
|
common hpath-io { build-depends: hpath-io >= 0.13.1 }
|
||||||
common hpath-posix { build-depends: hpath-posix >= 0.11.1 }
|
common hpath-posix { build-depends: hpath-posix >= 0.11.1 }
|
||||||
common http-io-streams { build-depends: http-io-streams >= 0.1 }
|
common http-io-streams { build-depends: http-io-streams >= 0.1.2.0 }
|
||||||
common io-streams { build-depends: io-streams >= 1.5 }
|
common io-streams { build-depends: io-streams >= 1.5 }
|
||||||
common language-bash { build-depends: language-bash >= 0.9 }
|
common language-bash { build-depends: language-bash >= 0.9 }
|
||||||
common lzma { build-depends: lzma >= 0.0.0.3 }
|
common lzma { build-depends: lzma >= 0.0.0.3 }
|
||||||
@@ -53,16 +55,18 @@ common pretty-terminal { build-depends: pretty-terminal >= 0.1.0.0 }
|
|||||||
common resourcet { build-depends: resourcet >= 1.2.2 }
|
common resourcet { build-depends: resourcet >= 1.2.2 }
|
||||||
common safe { build-depends: safe >= 0.3.18 }
|
common safe { build-depends: safe >= 0.3.18 }
|
||||||
common safe-exceptions { build-depends: safe-exceptions >= 0.1 }
|
common safe-exceptions { build-depends: safe-exceptions >= 0.1 }
|
||||||
common streamly { build-depends: streamly >= 0.7 }
|
common streamly { build-depends: streamly >= 0.7.1 }
|
||||||
common streamly-posix { build-depends: streamly-posix >= 0.1.0.0 }
|
common streamly-posix { build-depends: streamly-posix >= 0.1.0.0 }
|
||||||
common streamly-bytestring { build-depends: streamly-bytestring >= 0.1.2 }
|
common streamly-bytestring { build-depends: streamly-bytestring >= 0.1.2 }
|
||||||
common strict-base { build-depends: strict-base >= 0.4 }
|
common strict-base { build-depends: strict-base >= 0.4 }
|
||||||
common string-interpolate { build-depends: string-interpolate >= 0.2.0.0 }
|
common string-interpolate { build-depends: string-interpolate >= 0.2.0.0 }
|
||||||
common table-layout { build-depends: table-layout >= 0.8 }
|
common table-layout { build-depends: table-layout >= 0.8 }
|
||||||
common tar-bytestring { build-depends: tar-bytestring >= 0.6.2.0 }
|
common tar-bytestring { build-depends: tar-bytestring >= 0.6.3.0 }
|
||||||
common template-haskell { build-depends: template-haskell >= 2.7 }
|
common template-haskell { build-depends: template-haskell >= 2.7 }
|
||||||
|
common terminal-progress-bar { build-depends: terminal-progress-bar >= 0.4.1 }
|
||||||
common text { build-depends: text >= 1.2 }
|
common text { build-depends: text >= 1.2 }
|
||||||
common text-icu { build-depends: text-icu >= 0.7 }
|
common text-icu { build-depends: text-icu >= 0.7 }
|
||||||
|
common time { build-depends: time >= 1.9.3 }
|
||||||
common transformers { build-depends: transformers >= 0.5 }
|
common transformers { build-depends: transformers >= 0.5 }
|
||||||
common unix { build-depends: unix >= 2.7 }
|
common unix { build-depends: unix >= 2.7 }
|
||||||
common unix-bytestring { build-depends: unix-bytestring >= 0.3 }
|
common unix-bytestring { build-depends: unix-bytestring >= 0.3 }
|
||||||
@@ -96,8 +100,10 @@ library
|
|||||||
, ascii-string
|
, ascii-string
|
||||||
, async
|
, async
|
||||||
, attoparsec
|
, attoparsec
|
||||||
|
, binary
|
||||||
, bytestring
|
, bytestring
|
||||||
, bzlib
|
, bzlib
|
||||||
|
, case-insensitive
|
||||||
, containers
|
, containers
|
||||||
, generics-sop
|
, generics-sop
|
||||||
, haskus-utils-types
|
, haskus-utils-types
|
||||||
@@ -128,8 +134,10 @@ library
|
|||||||
, string-interpolate
|
, string-interpolate
|
||||||
, tar-bytestring
|
, tar-bytestring
|
||||||
, template-haskell
|
, template-haskell
|
||||||
|
, terminal-progress-bar
|
||||||
, text
|
, text
|
||||||
, text-icu
|
, text-icu
|
||||||
|
, time
|
||||||
, transformers
|
, transformers
|
||||||
, unix
|
, unix
|
||||||
, unix-bytestring
|
, unix-bytestring
|
||||||
@@ -211,9 +219,7 @@ executable ghcup-gen
|
|||||||
, uri-bytestring
|
, uri-bytestring
|
||||||
, utf8-string
|
, utf8-string
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules: BinaryDownloads
|
other-modules: GHCupDownloads
|
||||||
GHCupDownloads
|
|
||||||
SourceDownloads
|
|
||||||
Validate
|
Validate
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: ghcup
|
build-depends: ghcup
|
||||||
|
|||||||
584
lib/GHCup.hs
584
lib/GHCup.hs
@@ -8,7 +8,6 @@
|
|||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
-- TODO: handle SIGTERM, SIGUSR
|
|
||||||
module GHCup where
|
module GHCup where
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +22,7 @@ import GHCup.Utils.File
|
|||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
import GHCup.Utils.String.QQ
|
||||||
import GHCup.Utils.Version.QQ
|
import GHCup.Utils.Version.QQ
|
||||||
|
import GHCup.Version
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -34,7 +34,6 @@ import Control.Monad.Trans.Class ( lift )
|
|||||||
import Control.Monad.Trans.Resource
|
import Control.Monad.Trans.Resource
|
||||||
hiding ( throwM )
|
hiding ( throwM )
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Foldable
|
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
@@ -50,7 +49,6 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.Env.ByteString ( getEnvironment )
|
|
||||||
import System.Posix.FilePath ( getSearchPath )
|
import System.Posix.FilePath ( getSearchPath )
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import System.Posix.RawFilePath.Directory.Errors
|
||||||
( hideError )
|
( hideError )
|
||||||
@@ -65,55 +63,116 @@ import qualified Data.Text.Encoding as E
|
|||||||
--[ Tool installation ]--
|
--[ Tool installation ]--
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
-- TODO: custom logger intepreter and pretty printing
|
|
||||||
|
|
||||||
-- | Install a tool, such as GHC or cabal. This also sets
|
|
||||||
-- the ghc-x.y.z symlinks and potentially the ghc-x.y.
|
|
||||||
--
|
|
||||||
-- This can fail in many ways. You may want to explicitly catch
|
|
||||||
-- `AlreadyInstalled` to not make it fatal.
|
|
||||||
installTool :: ( MonadThrow m
|
|
||||||
, MonadReader Settings m
|
|
||||||
, MonadLogger m
|
|
||||||
, MonadCatch m
|
|
||||||
, MonadIO m
|
|
||||||
, MonadFail m
|
|
||||||
, MonadResource m
|
|
||||||
) -- tmp file
|
|
||||||
=> BinaryDownloads
|
|
||||||
-> ToolRequest
|
|
||||||
-> Maybe PlatformRequest -- ^ if Nothing, looks up current host platform
|
|
||||||
-> Excepts
|
|
||||||
'[ AlreadyInstalled
|
|
||||||
, ArchiveError
|
|
||||||
, DistroNotFound
|
|
||||||
, FileDoesNotExistError
|
|
||||||
, FileError
|
|
||||||
, JSONError
|
|
||||||
, NoCompatibleArch
|
|
||||||
, NoDownload
|
|
||||||
, NotInstalled
|
|
||||||
, PlatformResultError
|
|
||||||
, ProcessError
|
|
||||||
, URLException
|
|
||||||
, DigestError
|
|
||||||
]
|
|
||||||
m
|
|
||||||
()
|
|
||||||
installTool bDls treq mpfReq = do
|
|
||||||
lift $ $(logDebug) [i|Requested to install: #{treq}|]
|
|
||||||
|
|
||||||
-- stop if GHC is already installed, other tools can be overwritten
|
|
||||||
case treq of
|
|
||||||
(ToolRequest GHC _) ->
|
|
||||||
whenM (liftIO $ toolAlreadyInstalled treq)
|
|
||||||
$ (throwE $ AlreadyInstalled treq)
|
|
||||||
(ToolRequest Cabal _) -> pure ()
|
|
||||||
|
|
||||||
|
installGHCBin :: ( MonadFail m
|
||||||
|
, MonadMask m
|
||||||
|
, MonadCatch m
|
||||||
|
, MonadReader Settings m
|
||||||
|
, MonadLogger m
|
||||||
|
, MonadResource m
|
||||||
|
, MonadIO m
|
||||||
|
)
|
||||||
|
=> GHCupDownloads
|
||||||
|
-> Version
|
||||||
|
-> Maybe PlatformRequest -- ^ if Nothing, looks up current host platform
|
||||||
|
-> Excepts
|
||||||
|
'[ AlreadyInstalled
|
||||||
|
, BuildFailed
|
||||||
|
, DigestError
|
||||||
|
, DistroNotFound
|
||||||
|
, DownloadFailed
|
||||||
|
, NoCompatibleArch
|
||||||
|
, NoCompatiblePlatform
|
||||||
|
, NoDownload
|
||||||
|
, NotInstalled
|
||||||
|
, UnknownArchive
|
||||||
|
]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
installGHCBin bDls ver mpfReq = do
|
||||||
|
lift $ $(logDebug) [i|Requested to install GHC with #{ver}|]
|
||||||
|
whenM (liftIO $ toolAlreadyInstalled GHC ver)
|
||||||
|
$ (throwE $ AlreadyInstalled GHC ver)
|
||||||
Settings {..} <- lift ask
|
Settings {..} <- lift ask
|
||||||
|
|
||||||
-- download (or use cached version)
|
-- download (or use cached version)
|
||||||
dlinfo <- liftE $ getDownloadInfo bDls treq mpfReq
|
dlinfo <- liftE $ getDownloadInfo bDls GHC ver mpfReq
|
||||||
|
dl <- liftE $ downloadCached dlinfo Nothing
|
||||||
|
|
||||||
|
-- unpack
|
||||||
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
|
||||||
|
-- prepare paths
|
||||||
|
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||||
|
|
||||||
|
-- the subdir of the archive where we do the work
|
||||||
|
let archiveSubdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
|
catchAllE
|
||||||
|
(\es ->
|
||||||
|
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
|
>> throwE (BuildFailed archiveSubdir es)
|
||||||
|
)
|
||||||
|
$ installGHC' archiveSubdir ghcdir
|
||||||
|
|
||||||
|
-- only clean up dir if the build succeeded
|
||||||
|
liftIO $ deleteDirRecursive tmpUnpack
|
||||||
|
|
||||||
|
liftE $ postGHCInstall ver
|
||||||
|
|
||||||
|
where
|
||||||
|
-- | Install an unpacked GHC distribution. This only deals with the GHC build system and nothing else.
|
||||||
|
installGHC' :: (MonadLogger m, MonadIO m)
|
||||||
|
=> Path Abs -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
||||||
|
-> Path Abs -- ^ Path to install to
|
||||||
|
-> Excepts '[ProcessError] m ()
|
||||||
|
installGHC' path inst = do
|
||||||
|
lift $ $(logInfo) [s|Installing GHC (this may take a while)|]
|
||||||
|
lEM $ liftIO $ execLogged [s|./configure|]
|
||||||
|
False
|
||||||
|
[[s|--prefix=|] <> toFilePath inst]
|
||||||
|
([rel|ghc-configure.log|] :: Path Rel)
|
||||||
|
(Just path)
|
||||||
|
Nothing
|
||||||
|
lEM $ liftIO $ execLogged [s|make|]
|
||||||
|
True
|
||||||
|
[[s|install|]]
|
||||||
|
([rel|ghc-make.log|] :: Path Rel)
|
||||||
|
(Just path)
|
||||||
|
Nothing
|
||||||
|
pure ()
|
||||||
|
|
||||||
|
|
||||||
|
installCabalBin :: ( MonadMask m
|
||||||
|
, MonadCatch m
|
||||||
|
, MonadReader Settings m
|
||||||
|
, MonadLogger m
|
||||||
|
, MonadResource m
|
||||||
|
, MonadIO m
|
||||||
|
)
|
||||||
|
=> GHCupDownloads
|
||||||
|
-> Version
|
||||||
|
-> Maybe PlatformRequest -- ^ if Nothing, looks up current host platform
|
||||||
|
-> Excepts
|
||||||
|
'[ CopyError
|
||||||
|
, DigestError
|
||||||
|
, DistroNotFound
|
||||||
|
, DownloadFailed
|
||||||
|
, NoCompatibleArch
|
||||||
|
, NoCompatiblePlatform
|
||||||
|
, NoDownload
|
||||||
|
, UnknownArchive
|
||||||
|
]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
installCabalBin bDls ver mpfReq = do
|
||||||
|
lift $ $(logDebug) [i|Requested to install cabal version #{ver}|]
|
||||||
|
Settings {..} <- lift ask
|
||||||
|
|
||||||
|
-- download (or use cached version)
|
||||||
|
dlinfo <- liftE $ getDownloadInfo bDls Cabal ver mpfReq
|
||||||
dl <- liftE $ downloadCached dlinfo Nothing
|
dl <- liftE $ downloadCached dlinfo Nothing
|
||||||
|
|
||||||
-- unpack
|
-- unpack
|
||||||
@@ -121,62 +180,28 @@ installTool bDls treq mpfReq = do
|
|||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
|
||||||
-- prepare paths
|
-- prepare paths
|
||||||
ghcdir <- liftIO $ ghcupGHCDir (view trVersion $ treq)
|
|
||||||
bindir <- liftIO ghcupBinDir
|
bindir <- liftIO ghcupBinDir
|
||||||
|
|
||||||
-- the subdir of the archive where we do the work
|
-- the subdir of the archive where we do the work
|
||||||
let archiveSubdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
let archiveSubdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
case treq of
|
liftE $ installCabal' archiveSubdir bindir
|
||||||
(ToolRequest GHC ver) -> do
|
|
||||||
liftE $ installGHC archiveSubdir ghcdir
|
|
||||||
liftE $ postGHCInstall ver
|
|
||||||
(ToolRequest Cabal _) -> liftE $ installCabal archiveSubdir bindir
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
where
|
||||||
toolAlreadyInstalled :: ToolRequest -> IO Bool
|
-- | Install an unpacked cabal distribution.
|
||||||
toolAlreadyInstalled ToolRequest {..} = case _trTool of
|
installCabal' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
GHC -> ghcInstalled _trVersion
|
=> Path Abs -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
||||||
Cabal -> cabalInstalled _trVersion
|
-> Path Abs -- ^ Path to install to
|
||||||
|
-> Excepts '[CopyError] m ()
|
||||||
|
installCabal' path inst = do
|
||||||
|
lift $ $(logInfo) [s|Installing cabal|]
|
||||||
-- | Install an unpacked GHC distribution. This only deals with the GHC build system and nothing else.
|
let cabalFile = [rel|cabal|] :: Path Rel
|
||||||
installGHC :: (MonadLogger m, MonadIO m)
|
liftIO $ createDirIfMissing newDirPerms inst
|
||||||
=> Path Abs -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
-> Path Abs -- ^ Path to install to
|
(path </> cabalFile)
|
||||||
-> Excepts '[ProcessError] m ()
|
(inst </> cabalFile)
|
||||||
installGHC path inst = do
|
Overwrite
|
||||||
lift $ $(logInfo) [s|Installing GHC (this may take a while)|]
|
|
||||||
lEM $ liftIO $ execLogged [s|./configure|]
|
|
||||||
False
|
|
||||||
[[s|--prefix=|] <> toFilePath inst]
|
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
|
||||||
(Just path)
|
|
||||||
Nothing
|
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
|
||||||
True
|
|
||||||
[[s|install|]]
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just path)
|
|
||||||
Nothing
|
|
||||||
pure ()
|
|
||||||
|
|
||||||
|
|
||||||
-- | Install an unpacked cabal distribution.
|
|
||||||
installCabal :: (MonadLogger m, MonadCatch m, MonadIO m)
|
|
||||||
=> Path Abs -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
|
||||||
-> Path Abs -- ^ Path to install to
|
|
||||||
-> Excepts '[FileError] m ()
|
|
||||||
installCabal path inst = do
|
|
||||||
lift $ $(logInfo) [s|Installing cabal|]
|
|
||||||
let cabalFile = [rel|cabal|] :: Path Rel
|
|
||||||
liftIO $ createDirIfMissing newDirPerms inst
|
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
|
||||||
(path </> cabalFile)
|
|
||||||
(inst </> cabalFile)
|
|
||||||
Overwrite
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -190,12 +215,12 @@ installCabal path inst = do
|
|||||||
-- on `SetGHC`:
|
-- on `SetGHC`:
|
||||||
--
|
--
|
||||||
-- * SetGHCOnly: ~/.ghcup/bin/ghc -> ~/.ghcup/ghc/<ver>/bin/ghc
|
-- * SetGHCOnly: ~/.ghcup/bin/ghc -> ~/.ghcup/ghc/<ver>/bin/ghc
|
||||||
-- * SetGHCMajor: ~/.ghcup/bin/ghc-X.Y -> ~/.ghcup/ghc/<ver>/bin/ghc
|
-- * SetGHC_XY: ~/.ghcup/bin/ghc-X.Y -> ~/.ghcup/ghc/<ver>/bin/ghc
|
||||||
-- * SetGHCMinor: ~/.ghcup/bin/ghc-<ver> -> ~/.ghcup/ghc/<ver>/bin/ghc
|
-- * SetGHC_XYZ: ~/.ghcup/bin/ghc-<ver> -> ~/.ghcup/ghc/<ver>/bin/ghc
|
||||||
--
|
--
|
||||||
-- Additionally creates a ~/.ghcup/share -> ~/.ghcup/ghc/<ver>/share symlink
|
-- Additionally creates a ~/.ghcup/share -> ~/.ghcup/ghc/<ver>/share symlink
|
||||||
-- for `SetGHCOnly` constructor.
|
-- for `SetGHCOnly` constructor.
|
||||||
setGHC :: (MonadThrow m, MonadFail m, MonadIO m)
|
setGHC :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> Version
|
||||||
-> SetGHC
|
-> SetGHC
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
@@ -207,59 +232,58 @@ setGHC ver sghc = do
|
|||||||
bindir <- liftIO $ ghcupBinDir
|
bindir <- liftIO $ ghcupBinDir
|
||||||
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms bindir
|
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms bindir
|
||||||
|
|
||||||
when (sghc == SetGHCOnly) $ liftE (delOldSymlinks bindir)
|
-- first delete the old symlinks (this fixes compatibility issues
|
||||||
|
-- with old ghcup)
|
||||||
|
case sghc of
|
||||||
|
SetGHCOnly -> liftE $ rmPlain ver
|
||||||
|
SetGHC_XY -> lift $ rmMajorSymlinks ver
|
||||||
|
SetGHC_XYZ -> lift $ rmMinorSymlinks ver
|
||||||
|
|
||||||
-- for ghc tools (ghc, ghci, haddock, ...)
|
-- for ghc tools (ghc, ghci, haddock, ...)
|
||||||
verfiles <- ghcToolFiles ver
|
verfiles <- ghcToolFiles ver
|
||||||
forM_ verfiles $ \file -> do
|
forM_ verfiles $ \file -> do
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile (bindir </> file)
|
liftIO $ hideError doesNotExistErrorType $ deleteFile (bindir </> file)
|
||||||
targetFile <- case sghc of
|
targetFile <- case sghc of
|
||||||
SetGHCOnly -> pure file
|
SetGHCOnly -> pure file
|
||||||
SetGHCMajor -> do
|
SetGHC_XY -> do
|
||||||
major' <-
|
major' <-
|
||||||
(\(mj, mi) -> E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi)
|
(\(mj, mi) -> E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi)
|
||||||
<$> getGHCMajor ver
|
<$> getGHCMajor ver
|
||||||
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
||||||
SetGHCMinor -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
SetGHC_XYZ -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile
|
|
||||||
(bindir </> targetFile)
|
-- create symlink
|
||||||
liftIO $ createSymlink (bindir </> targetFile)
|
let fullF = bindir </> targetFile
|
||||||
(ghcLinkDestination (toFilePath file) ver)
|
let destL = ghcLinkDestination (toFilePath file) ver
|
||||||
|
lift $ $(logDebug) [i|ln -s #{destL} #{toFilePath fullF}|]
|
||||||
|
liftIO $ createSymlink fullF destL
|
||||||
|
|
||||||
-- create symlink for share dir
|
-- create symlink for share dir
|
||||||
liftIO $ symlinkShareDir ghcdir verBS
|
lift $ symlinkShareDir ghcdir verBS
|
||||||
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
symlinkShareDir :: Path Abs -> ByteString -> IO ()
|
symlinkShareDir :: (MonadIO m, MonadLogger m)
|
||||||
|
=> Path Abs
|
||||||
|
-> ByteString
|
||||||
|
-> m ()
|
||||||
symlinkShareDir ghcdir verBS = do
|
symlinkShareDir ghcdir verBS = do
|
||||||
destdir <- ghcupBaseDir
|
destdir <- liftIO $ ghcupBaseDir
|
||||||
case sghc of
|
case sghc of
|
||||||
SetGHCOnly -> do
|
SetGHCOnly -> do
|
||||||
let sharedir = [rel|share|] :: Path Rel
|
let sharedir = [rel|share|] :: Path Rel
|
||||||
let fullsharedir = ghcdir </> sharedir
|
let fullsharedir = ghcdir </> sharedir
|
||||||
whenM (doesDirectoryExist fullsharedir) $ do
|
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile
|
let fullF = destdir </> sharedir
|
||||||
(destdir </> sharedir)
|
let targetF = [s|./ghc/|] <> verBS <> [s|/|] <> toFilePath sharedir
|
||||||
createSymlink
|
$(logDebug) [i|rm -f #{fullF}|]
|
||||||
(destdir </> sharedir)
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
([s|./ghc/|] <> verBS <> [s|/|] <> toFilePath sharedir)
|
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
||||||
|
liftIO $ createSymlink fullF targetF
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
-- The old tool symlinks might be different (e.g. more) than the
|
|
||||||
-- requested version. Have to avoid "stray" symlinks.
|
|
||||||
delOldSymlinks :: forall m
|
|
||||||
. (MonadThrow m, MonadFail m, MonadIO m)
|
|
||||||
=> Path Abs
|
|
||||||
-> Excepts '[] m ()
|
|
||||||
delOldSymlinks bindir = catchLiftLeft (\NotInstalled{} -> pure ()) $ do
|
|
||||||
mv <- ghcSet
|
|
||||||
for_ mv $ \ver' -> do
|
|
||||||
verfiles <- ghcToolFiles ver'
|
|
||||||
for_ verfiles $ \f -> liftIO $ deleteFile (bindir </> f)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -278,17 +302,18 @@ data ListResult = ListResult
|
|||||||
, lTag :: [Tag]
|
, lTag :: [Tag]
|
||||||
, lInstalled :: Bool
|
, lInstalled :: Bool
|
||||||
, lSet :: Bool
|
, lSet :: Bool
|
||||||
|
, fromSrc :: Bool
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
availableToolVersions :: BinaryDownloads -> Tool -> [(Version, [Tag])]
|
availableToolVersions :: GHCupDownloads -> Tool -> [(Version, [Tag])]
|
||||||
availableToolVersions av tool = toListOf
|
availableToolVersions av tool = toListOf
|
||||||
(ix tool % to (fmap (\(v, vi) -> (v, (_viTags vi))) . Map.toList) % folded)
|
(ix tool % to (fmap (\(v, vi) -> (v, (_viTags vi))) . Map.toList) % folded)
|
||||||
av
|
av
|
||||||
|
|
||||||
|
|
||||||
listVersions :: BinaryDownloads
|
listVersions :: GHCupDownloads
|
||||||
-> Maybe Tool
|
-> Maybe Tool
|
||||||
-> Maybe ListCriteria
|
-> Maybe ListCriteria
|
||||||
-> IO [ListResult]
|
-> IO [ListResult]
|
||||||
@@ -298,7 +323,8 @@ listVersions av lt criteria = case lt of
|
|||||||
Nothing -> do
|
Nothing -> do
|
||||||
ghcvers <- listVersions av (Just GHC) criteria
|
ghcvers <- listVersions av (Just GHC) criteria
|
||||||
cabalvers <- listVersions av (Just Cabal) criteria
|
cabalvers <- listVersions av (Just Cabal) criteria
|
||||||
pure (ghcvers <> cabalvers)
|
ghcupvers <- listVersions av (Just GHCup) criteria
|
||||||
|
pure (ghcvers <> cabalvers <> ghcupvers)
|
||||||
|
|
||||||
where
|
where
|
||||||
toListResult :: Tool -> (Version, [Tag]) -> IO ListResult
|
toListResult :: Tool -> (Version, [Tag]) -> IO ListResult
|
||||||
@@ -306,11 +332,17 @@ listVersions av lt criteria = case lt of
|
|||||||
GHC -> do
|
GHC -> do
|
||||||
lSet <- fmap (maybe False (== v)) $ ghcSet
|
lSet <- fmap (maybe False (== v)) $ ghcSet
|
||||||
lInstalled <- ghcInstalled v
|
lInstalled <- ghcInstalled v
|
||||||
|
fromSrc <- ghcSrcInstalled v
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, .. }
|
pure ListResult { lVer = v, lTag = tags, lTool = t, .. }
|
||||||
Cabal -> do
|
Cabal -> do
|
||||||
lSet <- fmap (== v) $ cabalSet
|
lSet <- fmap (== v) $ cabalSet
|
||||||
lInstalled <- cabalInstalled v
|
lInstalled <- cabalInstalled v
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, .. }
|
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
||||||
|
GHCup -> do
|
||||||
|
let lSet = prettyPVP ghcUpVer == prettyVer v
|
||||||
|
let lInstalled = True
|
||||||
|
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
||||||
|
|
||||||
|
|
||||||
filter' :: [ListResult] -> [ListResult]
|
filter' :: [ListResult] -> [ListResult]
|
||||||
filter' lr = case criteria of
|
filter' lr = case criteria of
|
||||||
@@ -320,8 +352,6 @@ listVersions av lt criteria = case lt of
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------
|
--------------
|
||||||
--[ GHC rm ]--
|
--[ GHC rm ]--
|
||||||
--------------
|
--------------
|
||||||
@@ -335,9 +365,8 @@ rmGHCVer ver = do
|
|||||||
isSetGHC <- fmap (maybe False (== ver)) $ ghcSet
|
isSetGHC <- fmap (maybe False (== ver)) $ ghcSet
|
||||||
dir <- liftIO $ ghcupGHCDir ver
|
dir <- liftIO $ ghcupGHCDir ver
|
||||||
let d' = toFilePath dir
|
let d' = toFilePath dir
|
||||||
exists <- liftIO $ doesDirectoryExist dir
|
exists <- liftIO $ doesDirectoryExist dir
|
||||||
|
|
||||||
toolsFiles <- liftE $ ghcToolFiles ver
|
|
||||||
|
|
||||||
if exists
|
if exists
|
||||||
then do
|
then do
|
||||||
@@ -346,59 +375,27 @@ rmGHCVer ver = do
|
|||||||
liftIO $ deleteDirRecursive dir
|
liftIO $ deleteDirRecursive dir
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Removing ghc-x.y.z symlinks|]
|
lift $ $(logInfo) [i|Removing ghc-x.y.z symlinks|]
|
||||||
liftIO $ rmMinorSymlinks
|
lift $ rmMinorSymlinks ver
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Removing/rewiring ghc-x.y symlinks|]
|
lift $ $(logInfo) [i|Removing/rewiring ghc-x.y symlinks|]
|
||||||
liftE fixMajorSymlinks
|
-- first remove
|
||||||
|
lift $ rmMajorSymlinks ver
|
||||||
|
-- then fix them (e.g. with an earlier version)
|
||||||
|
(mj, mi) <- getGHCMajor ver
|
||||||
|
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
||||||
|
|
||||||
when isSetGHC $ liftE $ do
|
|
||||||
|
when isSetGHC $ do
|
||||||
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
||||||
rmPlain toolsFiles
|
liftE $ rmPlain ver
|
||||||
|
|
||||||
liftIO
|
liftIO
|
||||||
$ ghcupBaseDir
|
$ ghcupBaseDir
|
||||||
>>= hideError doesNotExistErrorType
|
>>= hideError doesNotExistErrorType
|
||||||
. deleteFile
|
. deleteFile
|
||||||
. (</> ([rel|share|] :: Path Rel))
|
. (</> ([rel|share|] :: Path Rel))
|
||||||
else throwE (NotInstalled $ ToolRequest GHC ver)
|
else throwE (NotInstalled GHC ver)
|
||||||
|
|
||||||
where
|
|
||||||
-- e.g. ghc-8.6.5
|
|
||||||
rmMinorSymlinks :: IO ()
|
|
||||||
rmMinorSymlinks = do
|
|
||||||
bindir <- ghcupBinDir
|
|
||||||
files <- getDirsFiles' bindir
|
|
||||||
let myfiles = filter
|
|
||||||
(\x -> ([s|-|] <> verToBS ver) `B.isSuffixOf` toFilePath x)
|
|
||||||
files
|
|
||||||
forM_ myfiles $ \f -> deleteFile (bindir </> f)
|
|
||||||
|
|
||||||
-- E.g. ghc, if this version is the set one.
|
|
||||||
-- This reads `ghcupGHCDir`.
|
|
||||||
rmPlain :: (MonadThrow m, MonadFail m, MonadIO m)
|
|
||||||
=> [Path Rel] -- ^ tools files
|
|
||||||
-> Excepts '[NotInstalled] m ()
|
|
||||||
rmPlain files = do
|
|
||||||
bindir <- liftIO $ ghcupBinDir
|
|
||||||
forM_ files $ \f -> liftIO $ deleteFile (bindir </> f)
|
|
||||||
|
|
||||||
-- e.g. ghc-8.6
|
|
||||||
fixMajorSymlinks :: (MonadFail m, MonadThrow m, MonadIO m)
|
|
||||||
=> Excepts '[NotInstalled] m ()
|
|
||||||
fixMajorSymlinks = do
|
|
||||||
(mj, mi) <- getGHCMajor ver
|
|
||||||
let v' = E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi
|
|
||||||
|
|
||||||
bindir <- liftIO $ ghcupBinDir
|
|
||||||
|
|
||||||
-- first delete them
|
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
|
||||||
let myfiles =
|
|
||||||
filter (\x -> ([s|-|] <> v') `B.isSuffixOf` toFilePath x) files
|
|
||||||
forM_ myfiles $ \f -> liftIO $ deleteFile (bindir </> f)
|
|
||||||
|
|
||||||
-- then fix them (e.g. with an earlier version)
|
|
||||||
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHCMajor)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -409,7 +406,7 @@ rmGHCVer ver = do
|
|||||||
|
|
||||||
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadReader Settings m, MonadIO m)
|
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadReader Settings m, MonadIO m)
|
||||||
=> Excepts
|
=> Excepts
|
||||||
'[PlatformResultError , NoCompatibleArch , DistroNotFound]
|
'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
||||||
m
|
m
|
||||||
DebugInfo
|
DebugInfo
|
||||||
getDebugInfo = do
|
getDebugInfo = do
|
||||||
@@ -430,38 +427,37 @@ getDebugInfo = do
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
||||||
compileGHC :: ( MonadReader Settings m
|
compileGHC :: ( MonadMask m
|
||||||
|
, MonadReader Settings m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadResource m
|
, MonadResource m
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> SourceDownloads
|
=> GHCupDownloads
|
||||||
-> Version -- ^ version to install
|
-> Version -- ^ version to install
|
||||||
-> Version -- ^ version to bootstrap with
|
-> Version -- ^ version to bootstrap with
|
||||||
-> Maybe Int -- ^ jobs
|
-> Maybe Int -- ^ jobs
|
||||||
-> Maybe (Path Abs) -- ^ build config
|
-> Maybe (Path Abs) -- ^ build config
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ AlreadyInstalled
|
'[ AlreadyInstalled
|
||||||
, NotInstalled
|
, BuildFailed
|
||||||
, GHCNotFound
|
|
||||||
, ArchiveError
|
|
||||||
, ProcessError
|
|
||||||
, URLException
|
|
||||||
, DigestError
|
, DigestError
|
||||||
, BuildConfigNotFound
|
, DownloadFailed
|
||||||
|
, GHCupSetError
|
||||||
|
, NoDownload
|
||||||
|
, UnknownArchive
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
compileGHC dls tver bver jobs mbuildConfig = do
|
compileGHC dls tver bver jobs mbuildConfig = do
|
||||||
let treq = ToolRequest GHC tver
|
|
||||||
lift $ $(logDebug) [i|Requested to compile: #{tver} with #{bver}|]
|
lift $ $(logDebug) [i|Requested to compile: #{tver} with #{bver}|]
|
||||||
alreadyInstalled <- liftIO $ toolAlreadyInstalled treq
|
whenM (liftIO $ toolAlreadyInstalled GHC tver)
|
||||||
when alreadyInstalled $ (throwE $ AlreadyInstalled treq)
|
(throwE $ AlreadyInstalled GHC tver)
|
||||||
|
|
||||||
-- download source tarball
|
-- download source tarball
|
||||||
dlInfo <- preview (ix tver) dls ?? GHCNotFound
|
dlInfo <- preview (ix GHC % ix tver % viSourceDL % _Just) dls ?? NoDownload
|
||||||
dl <- liftE $ downloadCached dlInfo Nothing
|
dl <- liftE $ downloadCached dlInfo Nothing
|
||||||
|
|
||||||
-- unpack
|
-- unpack
|
||||||
@@ -470,43 +466,20 @@ compileGHC dls tver bver jobs mbuildConfig = do
|
|||||||
|
|
||||||
bghc <- parseRel ([s|ghc-|] <> verToBS bver)
|
bghc <- parseRel ([s|ghc-|] <> verToBS bver)
|
||||||
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
||||||
|
|
||||||
ghcdir <- liftIO $ ghcupGHCDir tver
|
ghcdir <- liftIO $ ghcupGHCDir tver
|
||||||
if
|
|
||||||
| tver >= [vver|8.8.0|] -> do
|
|
||||||
cEnv <- liftIO $ getEnvironment
|
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
|
||||||
bghcPath <- (liftIO $ searchPath spaths bghc) !? GHCNotFound
|
|
||||||
let newEnv = ([s|GHC|], toFilePath bghcPath) : cEnv
|
|
||||||
lEM $ liftIO $ exec [s|./configure|]
|
|
||||||
False
|
|
||||||
[[s|--prefix=|] <> toFilePath ghcdir]
|
|
||||||
(Just workdir)
|
|
||||||
(Just newEnv)
|
|
||||||
| otherwise -> do
|
|
||||||
lEM $ liftIO $ exec
|
|
||||||
[s|./configure|]
|
|
||||||
False
|
|
||||||
[ [s|--prefix=|] <> toFilePath ghcdir
|
|
||||||
, [s|--with-ghc=|] <> toFilePath bghc
|
|
||||||
]
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
let build_mk = workdir </> ([rel|mk/build.mk|] :: Path Rel)
|
catchAllE
|
||||||
case mbuildConfig of
|
(\es ->
|
||||||
Just bc -> liftIO $ copyFile bc build_mk Overwrite
|
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
Nothing -> liftIO $ writeFile build_mk (Just newFilePerms) defaultConf
|
>> throwE (BuildFailed workdir es)
|
||||||
|
)
|
||||||
|
$ compile bghc ghcdir workdir
|
||||||
|
markSrcBuilt ghcdir workdir
|
||||||
|
|
||||||
lEM $ liftIO $ exec [s|make|]
|
-- only clean up dir if the build succeeded
|
||||||
True
|
liftIO $ deleteDirRecursive tmpUnpack
|
||||||
(maybe [] (\j -> [[s|-j|] <> fS (show j)]) jobs)
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
lEM $ liftIO $ exec [s|make|] True [[s|install|]] (Just workdir) Nothing
|
reThrowAll GHCupSetError $ postGHCInstall tver
|
||||||
|
|
||||||
liftE $ postGHCInstall tver
|
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
@@ -518,28 +491,160 @@ BUILD_SPHINX_PDF = NO
|
|||||||
HADDOCK_DOCS = YES
|
HADDOCK_DOCS = YES
|
||||||
GhcWithLlvmCodeGen = YES|]
|
GhcWithLlvmCodeGen = YES|]
|
||||||
|
|
||||||
|
compile :: (MonadCatch m, MonadLogger m, MonadIO m)
|
||||||
|
=> Path Rel
|
||||||
|
-> Path Abs
|
||||||
|
-> Path Abs
|
||||||
|
-> Excepts
|
||||||
|
'[NoDownload , FileDoesNotExistError , ProcessError]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
compile bghc ghcdir workdir = do
|
||||||
|
lift $ $(logInfo) [i|configuring build|]
|
||||||
|
if
|
||||||
|
| tver >= [vver|8.8.0|] -> do
|
||||||
|
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
||||||
|
bghcPath <- (liftIO $ searchPath spaths bghc) !? NoDownload
|
||||||
|
newEnv <- addToCurrentEnv [([s|GHC|], toFilePath bghcPath)]
|
||||||
|
lEM $ liftIO $ execLogged [s|./configure|]
|
||||||
|
False
|
||||||
|
[[s|--prefix=|] <> toFilePath ghcdir]
|
||||||
|
([rel|ghc-configure.log|] :: Path Rel)
|
||||||
|
(Just workdir)
|
||||||
|
(Just newEnv)
|
||||||
|
| otherwise -> do
|
||||||
|
lEM $ liftIO $ execLogged
|
||||||
|
[s|./configure|]
|
||||||
|
False
|
||||||
|
[ [s|--prefix=|] <> toFilePath ghcdir
|
||||||
|
, [s|--with-ghc=|] <> toFilePath bghc
|
||||||
|
]
|
||||||
|
([rel|ghc-configure.log|] :: Path Rel)
|
||||||
|
(Just workdir)
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
case mbuildConfig of
|
||||||
|
Just bc -> liftIOException
|
||||||
|
doesNotExistErrorType
|
||||||
|
(FileDoesNotExistError $ toFilePath bc)
|
||||||
|
(liftIO $ copyFile bc (build_mk workdir) Overwrite)
|
||||||
|
Nothing ->
|
||||||
|
liftIO $ writeFile (build_mk workdir) (Just newFilePerms) defaultConf
|
||||||
|
|
||||||
|
lift
|
||||||
|
$ $(logInfo)
|
||||||
|
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/ghc-make.log' to see the progress.|]
|
||||||
|
lEM $ liftIO $ execLogged [s|make|]
|
||||||
|
True
|
||||||
|
(maybe [] (\j -> [[s|-j|] <> fS (show j)]) jobs)
|
||||||
|
([rel|ghc-make.log|] :: Path Rel)
|
||||||
|
(Just workdir)
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
lift $ $(logInfo) [i|Installing...|]
|
||||||
|
lEM $ liftIO $ execLogged [s|make|]
|
||||||
|
True
|
||||||
|
[[s|install|]]
|
||||||
|
([rel|ghc-make.log|] :: Path Rel)
|
||||||
|
(Just workdir)
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
markSrcBuilt ghcdir workdir = do
|
||||||
|
let dest = (ghcdir </> ghcUpSrcBuiltFile)
|
||||||
|
liftIO $ copyFile (build_mk workdir) dest Overwrite
|
||||||
|
|
||||||
|
build_mk workdir = workdir </> ([rel|mk/build.mk|] :: Path Rel)
|
||||||
|
|
||||||
|
|
||||||
---------------
|
compileCabal :: ( MonadReader Settings m
|
||||||
--[ Set GHC ]--
|
, MonadResource m
|
||||||
---------------
|
, MonadMask m
|
||||||
|
, MonadLogger m
|
||||||
|
, MonadIO m
|
||||||
|
)
|
||||||
|
=> GHCupDownloads
|
||||||
|
-> Version -- ^ version to install
|
||||||
|
-> Version -- ^ GHC version to build with
|
||||||
|
-> Maybe Int
|
||||||
|
-> Excepts
|
||||||
|
'[ BuildFailed
|
||||||
|
, DigestError
|
||||||
|
, DownloadFailed
|
||||||
|
, NoDownload
|
||||||
|
, UnknownArchive
|
||||||
|
]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
compileCabal dls tver bver jobs = do
|
||||||
|
lift $ $(logDebug) [i|Requested to compile: #{tver} with ghc-#{bver}|]
|
||||||
|
|
||||||
|
-- download source tarball
|
||||||
|
dlInfo <- preview (ix Cabal % ix tver % viSourceDL % _Just) dls ?? NoDownload
|
||||||
|
dl <- liftE $ downloadCached dlInfo Nothing
|
||||||
|
|
||||||
|
-- unpack
|
||||||
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
|
||||||
|
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
||||||
|
|
||||||
|
reThrowAll (BuildFailed workdir) $ compile workdir
|
||||||
|
|
||||||
|
-- only clean up dir if the build succeeded
|
||||||
|
liftIO $ deleteDirRecursive tmpUnpack
|
||||||
|
|
||||||
|
pure ()
|
||||||
|
|
||||||
|
where
|
||||||
|
compile :: (MonadLogger m, MonadIO m)
|
||||||
|
=> Path Abs
|
||||||
|
-> Excepts '[ProcessError] m ()
|
||||||
|
compile workdir = do
|
||||||
|
lift
|
||||||
|
$ $(logInfo)
|
||||||
|
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/cabal-bootstrap.log' to see the progress.|]
|
||||||
|
|
||||||
|
let v' = verToBS bver
|
||||||
|
cabal_bin <- liftIO $ ghcupBinDir
|
||||||
|
newEnv <- lift $ addToCurrentEnv
|
||||||
|
[ ([s|GHC|] , [s|ghc-|] <> v')
|
||||||
|
, ([s|GHC_PKG|], [s|ghc-pkg-|] <> v')
|
||||||
|
, ([s|GHC_VER|], v')
|
||||||
|
, ([s|PREFIX|] , toFilePath cabal_bin)
|
||||||
|
]
|
||||||
|
|
||||||
|
lEM $ liftIO $ execLogged [s|./bootstrap.sh|]
|
||||||
|
False
|
||||||
|
(maybe [] (\j -> [[s|-j|], fS (show j)]) jobs)
|
||||||
|
([rel|cabal-bootstrap.log|] :: Path Rel)
|
||||||
|
(Just workdir)
|
||||||
|
(Just newEnv)
|
||||||
|
|
||||||
|
|
||||||
upgradeGHCup :: ( MonadReader Settings m
|
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
--[ Upgrade GHCup ]--
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
upgradeGHCup :: ( MonadMask m
|
||||||
|
, MonadReader Settings m
|
||||||
, MonadCatch m
|
, MonadCatch m
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadResource m
|
, MonadResource m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
)
|
)
|
||||||
=> BinaryDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe (Path Abs) -- ^ full file destination to write ghcup into
|
-> Maybe (Path Abs) -- ^ full file destination to write ghcup into
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ DigestError
|
'[ CopyError
|
||||||
, URLException
|
, DigestError
|
||||||
, DistroNotFound
|
, DistroNotFound
|
||||||
, PlatformResultError
|
, DownloadFailed
|
||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
|
, NoCompatiblePlatform
|
||||||
, NoDownload
|
, NoDownload
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
@@ -547,14 +652,16 @@ upgradeGHCup :: ( MonadReader Settings m
|
|||||||
upgradeGHCup dls mtarget = do
|
upgradeGHCup dls mtarget = do
|
||||||
lift $ $(logInfo) [i|Upgrading GHCup...|]
|
lift $ $(logInfo) [i|Upgrading GHCup...|]
|
||||||
let latestVer = head $ getTagged dls GHCup Latest
|
let latestVer = head $ getTagged dls GHCup Latest
|
||||||
dli <- liftE $ getDownloadInfo dls (ToolRequest GHCup latestVer) Nothing
|
dli <- liftE $ getDownloadInfo dls GHCup latestVer Nothing
|
||||||
tmp <- lift withGHCupTmpDir
|
tmp <- lift withGHCupTmpDir
|
||||||
let fn = [rel|ghcup|] :: Path Rel
|
let fn = [rel|ghcup|] :: Path Rel
|
||||||
p <- liftE $ download dli tmp (Just fn)
|
p <- liftE $ download dli tmp (Just fn)
|
||||||
case mtarget of
|
case mtarget of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
dest <- liftIO $ ghcupBinDir
|
dest <- liftIO $ ghcupBinDir
|
||||||
liftIO $ copyFile p (dest </> fn) Overwrite
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
||||||
|
(dest </> fn)
|
||||||
|
Overwrite
|
||||||
Just fullDest -> liftIO $ copyFile p fullDest Overwrite
|
Just fullDest -> liftIO $ copyFile p fullDest Overwrite
|
||||||
pure latestVer
|
pure latestVer
|
||||||
|
|
||||||
@@ -565,14 +672,15 @@ upgradeGHCup dls mtarget = do
|
|||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
||||||
-- | Creates ghc-x.y.z and ghc-x.y symlinks.
|
-- | Creates ghc-x.y.z and ghc-x.y symlinks. This is used for
|
||||||
postGHCInstall :: (MonadThrow m, MonadFail m, MonadIO m)
|
-- both installing from source and bindist.
|
||||||
|
postGHCInstall :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> Version
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
postGHCInstall ver = do
|
postGHCInstall ver = do
|
||||||
liftE $ setGHC ver SetGHCMinor
|
liftE $ setGHC ver SetGHC_XYZ
|
||||||
|
|
||||||
-- Create ghc-x.y symlinks. This may not be the current
|
-- Create ghc-x.y symlinks. This may not be the current
|
||||||
-- version, create it regardless.
|
-- version, create it regardless.
|
||||||
(mj, mi) <- liftIO $ getGHCMajor ver
|
(mj, mi) <- liftIO $ getGHCMajor ver
|
||||||
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHCMajor)
|
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCup.Download where
|
module GHCup.Download where
|
||||||
@@ -22,6 +23,7 @@ import GHCup.Utils.String.QQ
|
|||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
import Control.Monad.Fail ( MonadFail )
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Class ( lift )
|
import Control.Monad.Trans.Class ( lift )
|
||||||
@@ -30,9 +32,14 @@ import Control.Monad.Trans.Resource
|
|||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.ByteString.Builder
|
import Data.ByteString.Builder
|
||||||
|
import Data.CaseInsensitive ( CI )
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
|
import Data.Text.Read
|
||||||
|
import Data.Time.Clock
|
||||||
|
import Data.Time.Clock.POSIX
|
||||||
|
import Data.Time.Format
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
import HPath
|
||||||
@@ -52,24 +59,35 @@ import "unix-bytestring" System.Posix.IO.ByteString
|
|||||||
( fdWrite )
|
( fdWrite )
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
import System.Posix.RawFilePath.Directory.Errors
|
||||||
( hideError )
|
( hideError )
|
||||||
|
import System.ProgressBar
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
import URI.ByteString.QQ
|
import URI.ByteString.QQ
|
||||||
|
|
||||||
|
import qualified Data.Binary.Builder as B
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.IO.Streams as Streams
|
import qualified System.IO.Streams as Streams
|
||||||
|
import qualified System.Posix.Files.ByteString as PF
|
||||||
import qualified System.Posix.RawFilePath.Directory
|
import qualified System.Posix.RawFilePath.Directory
|
||||||
as RD
|
as RD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ghcupURL :: URI
|
ghcupURL :: URI
|
||||||
ghcupURL =
|
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.1.json|]
|
||||||
[uri|https://gist.githubusercontent.com/hasufell/5411271eb4ae52e16ad2200f80eb2813/raw/eb47b3c9d85edf3a4df2b869f8a8eda87fa94bb4/gistfile1.txt|]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Downloads the download information!
|
------------------
|
||||||
|
--[ High-level ]--
|
||||||
|
------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Downloads the download information! But only if we need to ;P
|
||||||
getDownloads :: ( FromJSONKey Tool
|
getDownloads :: ( FromJSONKey Tool
|
||||||
, FromJSONKey Version
|
, FromJSONKey Version
|
||||||
, FromJSON VersionInfo
|
, FromJSON VersionInfo
|
||||||
@@ -77,23 +95,113 @@ getDownloads :: ( FromJSONKey Tool
|
|||||||
, MonadCatch m
|
, MonadCatch m
|
||||||
, MonadReader Settings m
|
, MonadReader Settings m
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
|
, MonadThrow m
|
||||||
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> Excepts
|
=> Excepts '[JSONError , DownloadFailed] m GHCupDownloads
|
||||||
'[FileDoesNotExistError , URLException , JSONError]
|
|
||||||
m
|
|
||||||
GHCupDownloads
|
|
||||||
getDownloads = do
|
getDownloads = do
|
||||||
urlSource <- lift getUrlSource
|
urlSource <- lift getUrlSource
|
||||||
lift $ $(logDebug) [i|Receiving download info from: #{urlSource}|]
|
lift $ $(logDebug) [i|Receiving download info from: #{urlSource}|]
|
||||||
case urlSource of
|
case urlSource of
|
||||||
GHCupURL -> do
|
GHCupURL -> do
|
||||||
bs <- liftE $ downloadBS ghcupURL
|
bs <- reThrowAll DownloadFailed $ dl ghcupURL
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSource url) -> do
|
(OwnSource url) -> do
|
||||||
bs <- liftE $ downloadBS url
|
bs <- reThrowAll DownloadFailed $ dl url
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSpec av) -> pure $ av
|
(OwnSpec av) -> pure $ av
|
||||||
|
|
||||||
|
where
|
||||||
|
-- First check if the json file is in the ~/.ghcup/cache dir
|
||||||
|
-- and check it's access time. If it has been accessed within the
|
||||||
|
-- last 5 minutes, just reuse it.
|
||||||
|
--
|
||||||
|
-- If not, then send a HEAD request and check for modification time.
|
||||||
|
-- Only download the file if the modification time is newer
|
||||||
|
-- than the local file.
|
||||||
|
--
|
||||||
|
-- Always save the local file with the mod time of the remote file.
|
||||||
|
dl :: forall m1
|
||||||
|
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
||||||
|
=> URI
|
||||||
|
-> Excepts
|
||||||
|
'[ FileDoesNotExistError
|
||||||
|
, HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m1
|
||||||
|
L.ByteString
|
||||||
|
dl uri' = do
|
||||||
|
let path = view pathL' uri'
|
||||||
|
json_file <- (liftIO $ ghcupCacheDir)
|
||||||
|
>>= \cacheDir -> (cacheDir </>) <$> urlBaseName path
|
||||||
|
e <- liftIO $ doesFileExist json_file
|
||||||
|
if e
|
||||||
|
then do
|
||||||
|
accessTime <-
|
||||||
|
PF.accessTimeHiRes
|
||||||
|
<$> (liftIO $ PF.getFileStatus (toFilePath json_file))
|
||||||
|
currentTime <- liftIO $ getPOSIXTime
|
||||||
|
|
||||||
|
-- access time won't work on most linuxes, but we can try regardless
|
||||||
|
if (currentTime - accessTime) > 300
|
||||||
|
then do -- no access in last 5 minutes, re-check upstream mod time
|
||||||
|
getModTime >>= \case
|
||||||
|
Just modTime -> do
|
||||||
|
fileMod <- liftIO $ getModificationTime json_file
|
||||||
|
if modTime > fileMod
|
||||||
|
then do
|
||||||
|
bs <- liftE $ downloadBS uri'
|
||||||
|
liftIO $ writeFileWithModTime modTime json_file bs
|
||||||
|
pure bs
|
||||||
|
else liftIO $ readFile json_file
|
||||||
|
Nothing -> do
|
||||||
|
lift $ $(logWarn) [i|Unable to get/parse Last-Modified header|]
|
||||||
|
liftIO $ deleteFile json_file
|
||||||
|
liftE $ downloadBS uri'
|
||||||
|
else -- access in less than 5 minutes, re-use file
|
||||||
|
liftIO $ readFile json_file
|
||||||
|
else do
|
||||||
|
getModTime >>= \case
|
||||||
|
Just modTime -> do
|
||||||
|
bs <- liftE $ downloadBS uri'
|
||||||
|
liftIO $ writeFileWithModTime modTime json_file bs
|
||||||
|
pure bs
|
||||||
|
Nothing -> do
|
||||||
|
lift $ $(logWarn) [i|Unable to get/parse Last-Modified header|]
|
||||||
|
liftE $ downloadBS uri'
|
||||||
|
|
||||||
|
where
|
||||||
|
getModTime = do
|
||||||
|
headers <-
|
||||||
|
handleIO (\_ -> pure mempty)
|
||||||
|
$ liftE
|
||||||
|
$ ( catchAllE
|
||||||
|
(\_ ->
|
||||||
|
pure mempty :: Excepts '[] m1 (M.Map (CI ByteString) ByteString)
|
||||||
|
)
|
||||||
|
$ getHead uri'
|
||||||
|
)
|
||||||
|
pure $ parseModifiedHeader headers
|
||||||
|
|
||||||
|
|
||||||
|
parseModifiedHeader :: (M.Map (CI ByteString) ByteString) -> Maybe UTCTime
|
||||||
|
parseModifiedHeader headers =
|
||||||
|
(M.lookup (CI.mk [s|Last-Modified|]) headers) >>= \h -> parseTimeM
|
||||||
|
True
|
||||||
|
defaultTimeLocale
|
||||||
|
"%a, %d %b %Y %H:%M:%S %Z"
|
||||||
|
(T.unpack . E.decodeUtf8 $ h)
|
||||||
|
|
||||||
|
writeFileWithModTime :: UTCTime -> Path Abs -> L.ByteString -> IO ()
|
||||||
|
writeFileWithModTime utctime path content = do
|
||||||
|
let mod_time = utcTimeToPOSIXSeconds utctime
|
||||||
|
writeFileL path (Just newFilePerms) content
|
||||||
|
setModificationTimeHiRes path mod_time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getDownloadInfo :: ( MonadLogger m
|
getDownloadInfo :: ( MonadLogger m
|
||||||
@@ -101,18 +209,19 @@ getDownloadInfo :: ( MonadLogger m
|
|||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadReader Settings m
|
, MonadReader Settings m
|
||||||
)
|
)
|
||||||
=> BinaryDownloads
|
=> GHCupDownloads
|
||||||
-> ToolRequest
|
-> Tool
|
||||||
|
-> Version
|
||||||
-> Maybe PlatformRequest
|
-> Maybe PlatformRequest
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ DistroNotFound
|
'[ DistroNotFound
|
||||||
, PlatformResultError
|
, NoCompatiblePlatform
|
||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoDownload
|
, NoDownload
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
DownloadInfo
|
DownloadInfo
|
||||||
getDownloadInfo bDls (ToolRequest t v) mpfReq = do
|
getDownloadInfo bDls t v mpfReq = do
|
||||||
(PlatformRequest arch' plat ver) <- case mpfReq of
|
(PlatformRequest arch' plat ver) <- case mpfReq of
|
||||||
Just x -> pure x
|
Just x -> pure x
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
@@ -132,7 +241,7 @@ getDownloadInfo' :: Tool
|
|||||||
-- ^ user platform
|
-- ^ user platform
|
||||||
-> Maybe Versioning
|
-> Maybe Versioning
|
||||||
-- ^ optional version of the platform
|
-- ^ optional version of the platform
|
||||||
-> BinaryDownloads
|
-> GHCupDownloads
|
||||||
-> Either NoDownload DownloadInfo
|
-> Either NoDownload DownloadInfo
|
||||||
getDownloadInfo' t v a p mv dls = maybe
|
getDownloadInfo' t v a p mv dls = maybe
|
||||||
(Left NoDownload)
|
(Left NoDownload)
|
||||||
@@ -155,15 +264,21 @@ getDownloadInfo' t v a p mv dls = maybe
|
|||||||
-- 2. otherwise create a random file
|
-- 2. otherwise create a random file
|
||||||
--
|
--
|
||||||
-- The file must not exist.
|
-- The file must not exist.
|
||||||
download :: (MonadReader Settings m, MonadThrow m, MonadLogger m, MonadIO m)
|
download :: ( MonadMask m
|
||||||
|
, MonadReader Settings m
|
||||||
|
, MonadThrow m
|
||||||
|
, MonadLogger m
|
||||||
|
, MonadIO m
|
||||||
|
)
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Path Abs -- ^ destination dir
|
-> Path Abs -- ^ destination dir
|
||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe (Path Rel) -- ^ optional filename
|
||||||
-> Excepts '[DigestError , URLException] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
||||||
download dli dest mfn | scheme == [s|https|] = dl True
|
download dli dest mfn
|
||||||
| scheme == [s|http|] = dl False
|
| scheme == [s|https|] = dl
|
||||||
| scheme == [s|file|] = cp
|
| scheme == [s|http|] = dl
|
||||||
| otherwise = throwE UnsupportedURL
|
| scheme == [s|file|] = cp
|
||||||
|
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
||||||
|
|
||||||
where
|
where
|
||||||
scheme = view (dlUri % uriSchemeL' % schemeBSL') dli
|
scheme = view (dlUri % uriSchemeL' % schemeBSL') dli
|
||||||
@@ -174,16 +289,12 @@ download dli dest mfn | scheme == [s|https|] = dl True
|
|||||||
fromFile <- parseAbs path
|
fromFile <- parseAbs path
|
||||||
liftIO $ copyFile fromFile destFile Strict
|
liftIO $ copyFile fromFile destFile Strict
|
||||||
pure destFile
|
pure destFile
|
||||||
dl https = do
|
dl = do
|
||||||
let uri' = E.decodeUtf8 (serializeURIRef' (view dlUri dli))
|
let uri' = E.decodeUtf8 (serializeURIRef' (view dlUri dli))
|
||||||
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
||||||
|
|
||||||
host <-
|
(https, host, fullPath, port) <- reThrowAll DownloadFailed
|
||||||
preview (dlUri % authorityL' % _Just % authorityHostL' % hostBSL') dli
|
$ uriToQuadruple (view dlUri dli)
|
||||||
?? UnsupportedURL
|
|
||||||
let port = preview
|
|
||||||
(dlUri % authorityL' % _Just % authorityPortL' % _Just % portNumberL')
|
|
||||||
dli
|
|
||||||
|
|
||||||
-- destination dir must exist
|
-- destination dir must exist
|
||||||
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
||||||
@@ -192,13 +303,10 @@ download dli dest mfn | scheme == [s|https|] = dl True
|
|||||||
-- download
|
-- download
|
||||||
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
||||||
let stepper = fdWrite fd
|
let stepper = fdWrite fd
|
||||||
liftIO $ flip finally (closeFd fd) $ downloadInternal https
|
flip finally (liftIO $ closeFd fd)
|
||||||
host
|
$ reThrowAll DownloadFailed
|
||||||
path
|
$ downloadInternal True https host fullPath port stepper
|
||||||
port
|
|
||||||
stepper
|
|
||||||
|
|
||||||
-- TODO: verify md5 during download
|
|
||||||
liftE $ checkDigest dli destFile
|
liftE $ checkDigest dli destFile
|
||||||
pure destFile
|
pure destFile
|
||||||
|
|
||||||
@@ -211,7 +319,8 @@ download dli dest mfn | scheme == [s|https|] = dl True
|
|||||||
|
|
||||||
-- | Download into tmpdir or use cached version, if it exists. If filename
|
-- | Download into tmpdir or use cached version, if it exists. If filename
|
||||||
-- is omitted, infers the filename from the url.
|
-- is omitted, infers the filename from the url.
|
||||||
downloadCached :: ( MonadResource m
|
downloadCached :: ( MonadMask m
|
||||||
|
, MonadResource m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
@@ -219,7 +328,7 @@ downloadCached :: ( MonadResource m
|
|||||||
)
|
)
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe (Path Rel) -- ^ optional filename
|
||||||
-> Excepts '[DigestError , URLException] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
||||||
downloadCached dli mfn = do
|
downloadCached dli mfn = do
|
||||||
cache <- lift getCache
|
cache <- lift getCache
|
||||||
case cache of
|
case cache of
|
||||||
@@ -238,11 +347,24 @@ downloadCached dli mfn = do
|
|||||||
liftE $ download dli tmp mfn
|
liftE $ download dli tmp mfn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------
|
||||||
|
--[ Low-level ]--
|
||||||
|
------------------
|
||||||
|
|
||||||
|
|
||||||
-- | This is used for downloading the JSON.
|
-- | This is used for downloading the JSON.
|
||||||
downloadBS :: (MonadCatch m, MonadIO m)
|
downloadBS :: (MonadCatch m, MonadIO m)
|
||||||
=> URI
|
=> URI
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[FileDoesNotExistError , URLException]
|
'[ FileDoesNotExistError
|
||||||
|
, HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
m
|
m
|
||||||
L.ByteString
|
L.ByteString
|
||||||
downloadBS uri'
|
downloadBS uri'
|
||||||
@@ -251,66 +373,231 @@ downloadBS uri'
|
|||||||
| scheme == [s|http|]
|
| scheme == [s|http|]
|
||||||
= dl False
|
= dl False
|
||||||
| scheme == [s|file|]
|
| scheme == [s|file|]
|
||||||
= liftException doesNotExistErrorType (FileDoesNotExistError path)
|
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
||||||
$ (liftIO $ RD.readFile path :: MonadIO m => Excepts '[] m L.ByteString)
|
$ (liftIO $ RD.readFile path)
|
||||||
| otherwise
|
| otherwise
|
||||||
= throwE UnsupportedURL
|
= throwE UnsupportedScheme
|
||||||
|
|
||||||
where
|
where
|
||||||
scheme = view (uriSchemeL' % schemeBSL') uri'
|
scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||||
path = view pathL' uri'
|
path = view pathL' uri'
|
||||||
dl https = do
|
dl https = do
|
||||||
host <-
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
preview (authorityL' % _Just % authorityHostL' % hostBSL') uri'
|
liftE $ downloadBS' https host' fullPath' port'
|
||||||
?? UnsupportedURL
|
|
||||||
let port = preview
|
|
||||||
(authorityL' % _Just % authorityPortL' % _Just % portNumberL')
|
|
||||||
uri'
|
|
||||||
liftIO $ downloadBS' https host path port
|
|
||||||
|
|
||||||
|
|
||||||
-- | Load the result of this download into memory at once.
|
-- | Load the result of this download into memory at once.
|
||||||
downloadBS' :: Bool -- ^ https?
|
downloadBS' :: MonadIO m
|
||||||
|
=> Bool -- ^ https?
|
||||||
-> ByteString -- ^ host (e.g. "www.example.com")
|
-> ByteString -- ^ host (e.g. "www.example.com")
|
||||||
-> ByteString -- ^ path (e.g. "/my/file")
|
-> ByteString -- ^ path (e.g. "/my/file") including query
|
||||||
-> Maybe Int -- ^ optional port (e.g. 3000)
|
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||||
-> IO (L.ByteString)
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(L.ByteString)
|
||||||
downloadBS' https host path port = do
|
downloadBS' https host path port = do
|
||||||
bref <- newIORef (mempty :: Builder)
|
bref <- liftIO $ newIORef (mempty :: Builder)
|
||||||
let stepper bs = modifyIORef bref (<> byteString bs)
|
let stepper bs = modifyIORef bref (<> byteString bs)
|
||||||
downloadInternal https host path port stepper
|
downloadInternal False https host path port stepper
|
||||||
readIORef bref <&> toLazyByteString
|
liftIO (readIORef bref <&> toLazyByteString)
|
||||||
|
|
||||||
|
|
||||||
downloadInternal :: Bool
|
downloadInternal :: MonadIO m
|
||||||
-> ByteString
|
=> Bool -- ^ whether to show a progress bar
|
||||||
-> ByteString
|
-> Bool -- ^ https?
|
||||||
-> Maybe Int
|
-> ByteString -- ^ host
|
||||||
-> (ByteString -> IO a) -- ^ the consuming step function
|
-> ByteString -- ^ path with query
|
||||||
-> IO ()
|
-> Maybe Int -- ^ optional port
|
||||||
downloadInternal https host path port consumer = do
|
-> (ByteString -> IO a) -- ^ the consuming step function
|
||||||
c <- case https of
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
downloadInternal = go (5 :: Int)
|
||||||
|
|
||||||
|
where
|
||||||
|
go redirs progressBar https host path port consumer = do
|
||||||
|
r <- liftIO $ withConnection' https host port action
|
||||||
|
veitherToExcepts r >>= \case
|
||||||
|
Just r' ->
|
||||||
|
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
||||||
|
Nothing -> pure ()
|
||||||
|
where
|
||||||
|
action c = do
|
||||||
|
let q = buildRequest1 $ http GET path
|
||||||
|
|
||||||
|
sendRequest c q emptyBody
|
||||||
|
|
||||||
|
receiveResponse
|
||||||
|
c
|
||||||
|
(\r i' -> runE $ do
|
||||||
|
let scode = getStatusCode r
|
||||||
|
if
|
||||||
|
| scode >= 200 && scode < 300 -> downloadStream r i' >> pure Nothing
|
||||||
|
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
||||||
|
Just r' -> pure $ Just $ r'
|
||||||
|
Nothing -> throwE NoLocationHeader
|
||||||
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
|
)
|
||||||
|
|
||||||
|
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
||||||
|
Right uri' -> do
|
||||||
|
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
go (redirs - 1) progressBar https' host' fullPath' port' consumer
|
||||||
|
Left e -> throwE e
|
||||||
|
|
||||||
|
downloadStream r i' = do
|
||||||
|
let size = case getHeader r [s|Content-Length|] of
|
||||||
|
Just x' -> case decimal $ E.decodeUtf8 x' of
|
||||||
|
Left _ -> 0
|
||||||
|
Right (r', _) -> r'
|
||||||
|
Nothing -> 0
|
||||||
|
|
||||||
|
mpb <- if progressBar
|
||||||
|
then Just <$> (liftIO $ newProgressBar defStyle 10 (Progress 0 size ()))
|
||||||
|
else pure Nothing
|
||||||
|
|
||||||
|
outStream <- liftIO $ Streams.makeOutputStream
|
||||||
|
(\case
|
||||||
|
Just bs -> do
|
||||||
|
forM_ mpb $ \pb -> incProgress pb (BS.length bs)
|
||||||
|
void $ consumer bs
|
||||||
|
Nothing -> pure ()
|
||||||
|
)
|
||||||
|
liftIO $ Streams.connect i' outStream
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
getHead :: (MonadCatch m, MonadIO m)
|
||||||
|
=> URI
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(M.Map (CI ByteString) ByteString)
|
||||||
|
getHead uri' | scheme == [s|https|] = head' True
|
||||||
|
| scheme == [s|http|] = head' False
|
||||||
|
| otherwise = throwE UnsupportedScheme
|
||||||
|
|
||||||
|
where
|
||||||
|
scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||||
|
head' https = do
|
||||||
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
liftE $ headInternal https host' fullPath' port'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
headInternal :: MonadIO m
|
||||||
|
=> Bool -- ^ https?
|
||||||
|
-> ByteString -- ^ host
|
||||||
|
-> ByteString -- ^ path with query
|
||||||
|
-> Maybe Int -- ^ optional port
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, TooManyRedirs
|
||||||
|
, NoLocationHeader
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(M.Map (CI ByteString) ByteString)
|
||||||
|
headInternal = go (5 :: Int)
|
||||||
|
|
||||||
|
where
|
||||||
|
go redirs https host path port = do
|
||||||
|
r <- liftIO $ withConnection' https host port action
|
||||||
|
veitherToExcepts r >>= \case
|
||||||
|
Left r' ->
|
||||||
|
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
||||||
|
Right hs -> pure hs
|
||||||
|
where
|
||||||
|
|
||||||
|
action c = do
|
||||||
|
let q = buildRequest1 $ http HEAD path
|
||||||
|
|
||||||
|
sendRequest c q emptyBody
|
||||||
|
|
||||||
|
unsafeReceiveResponse
|
||||||
|
c
|
||||||
|
(\r _ -> runE $ do
|
||||||
|
let scode = getStatusCode r
|
||||||
|
if
|
||||||
|
| scode >= 200 && scode < 300 -> do
|
||||||
|
let headers = getHeaderMap r
|
||||||
|
pure $ Right $ headers
|
||||||
|
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
||||||
|
Just r' -> pure $ Left $ r'
|
||||||
|
Nothing -> throwE NoLocationHeader
|
||||||
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
|
)
|
||||||
|
|
||||||
|
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
||||||
|
Right uri' -> do
|
||||||
|
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
go (redirs - 1) https' host' fullPath' port'
|
||||||
|
Left e -> throwE e
|
||||||
|
|
||||||
|
|
||||||
|
withConnection' :: Bool
|
||||||
|
-> ByteString
|
||||||
|
-> Maybe Int
|
||||||
|
-> (Connection -> IO a)
|
||||||
|
-> IO a
|
||||||
|
withConnection' https host port action = bracket acquire closeConnection action
|
||||||
|
|
||||||
|
where
|
||||||
|
acquire = case https of
|
||||||
True -> do
|
True -> do
|
||||||
ctx <- baselineContextSSL
|
ctx <- baselineContextSSL
|
||||||
openConnectionSSL ctx host (fromIntegral $ fromMaybe 443 port)
|
openConnectionSSL ctx host (fromIntegral $ fromMaybe 443 port)
|
||||||
False -> openConnection host (fromIntegral $ fromMaybe 80 port)
|
False -> openConnection host (fromIntegral $ fromMaybe 80 port)
|
||||||
|
|
||||||
let q = buildRequest1 $ http GET path
|
|
||||||
|
|
||||||
sendRequest c q emptyBody
|
-- | Extracts from a URI type: (https?, host, path+query, port)
|
||||||
|
uriToQuadruple :: Monad m
|
||||||
|
=> URI
|
||||||
|
-> Excepts
|
||||||
|
'[UnsupportedScheme]
|
||||||
|
m
|
||||||
|
(Bool, ByteString, ByteString, Maybe Int)
|
||||||
|
uriToQuadruple URI {..} = do
|
||||||
|
let scheme = view schemeBSL' uriScheme
|
||||||
|
|
||||||
receiveResponse
|
host <-
|
||||||
c
|
preview (_Just % authorityHostL' % hostBSL') uriAuthority
|
||||||
(\_ i' -> do
|
?? UnsupportedScheme
|
||||||
outStream <- Streams.makeOutputStream
|
|
||||||
(\case
|
|
||||||
Just bs -> void $ consumer bs
|
|
||||||
Nothing -> pure ()
|
|
||||||
)
|
|
||||||
Streams.connect i' outStream
|
|
||||||
)
|
|
||||||
|
|
||||||
closeConnection c
|
https <- if
|
||||||
|
| scheme == [s|https|] -> pure True
|
||||||
|
| scheme == [s|http|] -> pure False
|
||||||
|
| otherwise -> throwE UnsupportedScheme
|
||||||
|
|
||||||
|
let
|
||||||
|
queryBS =
|
||||||
|
BS.intercalate [s|&|]
|
||||||
|
. fmap (\(x, y) -> encodeQuery x <> [s|=|] <> encodeQuery y)
|
||||||
|
$ (queryPairs uriQuery)
|
||||||
|
port =
|
||||||
|
preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority
|
||||||
|
fullpath =
|
||||||
|
if BS.null queryBS then uriPath else uriPath <> [s|?|] <> queryBS
|
||||||
|
pure (https, host, fullpath, port)
|
||||||
|
where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
|
||||||
|
|
||||||
|
|
||||||
checkDigest :: (MonadIO m, MonadLogger m, MonadReader Settings m)
|
checkDigest :: (MonadIO m, MonadLogger m, MonadReader Settings m)
|
||||||
@@ -326,4 +613,3 @@ checkDigest dli file = do
|
|||||||
let cDigest = E.decodeUtf8 . toHex . digest (digestByName "sha256") $ c
|
let cDigest = E.decodeUtf8 . toHex . digest (digestByName "sha256") $ c
|
||||||
eDigest = view dlHash dli
|
eDigest = view dlHash dli
|
||||||
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
{-# LANGUAGE ExistentialQuantification #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
|
||||||
module GHCup.Errors where
|
module GHCup.Errors where
|
||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
@@ -5,59 +10,115 @@ import GHCup.Types
|
|||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
|
import Data.Versions
|
||||||
|
import Haskus.Utils.Variant
|
||||||
import HPath
|
import HPath
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
--[ Low-level errors ]--
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | A compatible platform could not be found.
|
-- | A compatible platform could not be found.
|
||||||
data PlatformResultError = NoCompatiblePlatform String -- the platform we got
|
data NoCompatiblePlatform = NoCompatiblePlatform String -- the platform we got
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | Unable to find a download for the requested versio/distro.
|
||||||
data NoDownload = NoDownload
|
data NoDownload = NoDownload
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | The Architecture is unknown and unsupported.
|
||||||
data NoCompatibleArch = NoCompatibleArch String
|
data NoCompatibleArch = NoCompatibleArch String
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | Unable to figure out the distribution of the host.
|
||||||
data DistroNotFound = DistroNotFound
|
data DistroNotFound = DistroNotFound
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data ArchiveError = UnknownArchive ByteString
|
-- | The archive format is unknown. We don't know how to extract it.
|
||||||
|
data UnknownArchive = UnknownArchive ByteString
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data URLException = UnsupportedURL
|
-- | The scheme is not supported (such as ftp).
|
||||||
|
data UnsupportedScheme = UnsupportedScheme
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data FileError = CopyError String
|
-- | Unable to copy a file.
|
||||||
|
data CopyError = CopyError String
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | Unable to find a tag of a tool.
|
||||||
data TagNotFound = TagNotFound Tag Tool
|
data TagNotFound = TagNotFound Tag Tool
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data AlreadyInstalled = AlreadyInstalled ToolRequest
|
-- | The tool (such as GHC) is already installed with that version.
|
||||||
|
data AlreadyInstalled = AlreadyInstalled Tool Version
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
data NotInstalled = NotInstalled ToolRequest
|
-- | The tool is not installed. Some operations rely on a tool
|
||||||
deriving Show
|
-- to be installed (such as setting the current GHC version).
|
||||||
|
data NotInstalled = NotInstalled Tool Version
|
||||||
data NotSet = NotSet Tool
|
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | JSON decoding failed.
|
||||||
data JSONError = JSONDecodeError String
|
data JSONError = JSONDecodeError String
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | A file that is supposed to exist does not exist
|
||||||
|
-- (e.g. when we use file scheme to "download" something).
|
||||||
|
data FileDoesNotExistError = FileDoesNotExistError ByteString
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | File digest verification failed.
|
||||||
|
data DigestError = DigestError Text Text
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | Unexpected HTTP status.
|
||||||
|
data HTTPStatusError = HTTPStatusError Int
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | The 'Location' header was expected during a 3xx redirect, but not found.
|
||||||
|
data NoLocationHeader = NoLocationHeader
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | Too many redirects.
|
||||||
|
data TooManyRedirs = TooManyRedirs
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
--[ High-level errors ]--
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
-- | A download failed. The underlying error is encapsulated.
|
||||||
|
data DownloadFailed = forall es . Show (V es) => DownloadFailed (V es)
|
||||||
|
|
||||||
|
deriving instance Show DownloadFailed
|
||||||
|
|
||||||
|
|
||||||
|
-- | A build failed.
|
||||||
|
data BuildFailed = forall es . Show (V es) => BuildFailed (Path Abs) (V es)
|
||||||
|
|
||||||
|
deriving instance Show BuildFailed
|
||||||
|
|
||||||
|
|
||||||
|
-- | Setting the current GHC version failed.
|
||||||
|
data GHCupSetError = forall es . Show (V es) => GHCupSetError (V es)
|
||||||
|
|
||||||
|
deriving instance Show GHCupSetError
|
||||||
|
|
||||||
|
|
||||||
|
---------------------------------------------
|
||||||
|
--[ True Exceptions (e.g. for MonadThrow) ]--
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Parsing failed.
|
||||||
data ParseError = ParseError String
|
data ParseError = ParseError String
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
instance Exception ParseError
|
instance Exception ParseError
|
||||||
|
|
||||||
data FileDoesNotExistError = FileDoesNotExistError ByteString
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
data GHCNotFound = GHCNotFound
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
data BuildConfigNotFound = BuildConfigNotFound (Path Abs)
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
data DigestError = DigestError Text Text
|
|
||||||
deriving Show
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ getArchitecture = case arch of
|
|||||||
|
|
||||||
getPlatform :: (MonadLogger m, MonadCatch m, MonadIO m)
|
getPlatform :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Excepts
|
=> Excepts
|
||||||
'[PlatformResultError , DistroNotFound]
|
'[NoCompatiblePlatform , DistroNotFound]
|
||||||
m
|
m
|
||||||
PlatformResult
|
PlatformResult
|
||||||
getPlatform = do
|
getPlatform = do
|
||||||
|
|||||||
@@ -12,6 +12,97 @@ import qualified GHC.Generics as GHC
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
--[ Download Tree ]--
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Description of all binary and source downloads. This is a tree
|
||||||
|
-- of nested maps.
|
||||||
|
type GHCupDownloads = Map Tool ToolVersionSpec
|
||||||
|
type ToolVersionSpec = Map Version VersionInfo
|
||||||
|
type ArchitectureSpec = Map Architecture PlatformSpec
|
||||||
|
type PlatformSpec = Map Platform PlatformVersionSpec
|
||||||
|
type PlatformVersionSpec = Map (Maybe Versioning) DownloadInfo
|
||||||
|
|
||||||
|
|
||||||
|
-- | An installable tool.
|
||||||
|
data Tool = GHC
|
||||||
|
| Cabal
|
||||||
|
| GHCup
|
||||||
|
deriving (Eq, GHC.Generic, Ord, Show)
|
||||||
|
|
||||||
|
|
||||||
|
-- | All necessary information of a tool version, including
|
||||||
|
-- source download and per-architecture downloads.
|
||||||
|
data VersionInfo = VersionInfo
|
||||||
|
{ _viTags :: [Tag] -- ^ version specific tag
|
||||||
|
, _viSourceDL :: Maybe DownloadInfo -- ^ source tarball
|
||||||
|
, _viArch :: ArchitectureSpec -- ^ descend for binary downloads per arch
|
||||||
|
}
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
|
-- | A tag. These are currently attached to a version of a tool.
|
||||||
|
data Tag = Latest
|
||||||
|
| Recommended
|
||||||
|
deriving (Ord, Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
|
data Architecture = A_64
|
||||||
|
| A_32
|
||||||
|
deriving (Eq, GHC.Generic, Ord, Show)
|
||||||
|
|
||||||
|
|
||||||
|
data Platform = Linux LinuxDistro
|
||||||
|
-- ^ must exit
|
||||||
|
| Darwin
|
||||||
|
-- ^ must exit
|
||||||
|
| FreeBSD
|
||||||
|
deriving (Eq, GHC.Generic, Ord, Show)
|
||||||
|
|
||||||
|
data LinuxDistro = Debian
|
||||||
|
| Ubuntu
|
||||||
|
| Mint
|
||||||
|
| Fedora
|
||||||
|
| CentOS
|
||||||
|
| RedHat
|
||||||
|
| Alpine
|
||||||
|
| AmazonLinux
|
||||||
|
-- rolling
|
||||||
|
| Gentoo
|
||||||
|
| Exherbo
|
||||||
|
-- not known
|
||||||
|
| UnknownLinux
|
||||||
|
-- ^ must exit
|
||||||
|
deriving (Eq, GHC.Generic, Ord, Show)
|
||||||
|
|
||||||
|
|
||||||
|
-- | An encapsulation of a download. This can be used
|
||||||
|
-- to download, extract and install a tool.
|
||||||
|
data DownloadInfo = DownloadInfo
|
||||||
|
{ _dlUri :: URI
|
||||||
|
, _dlSubdir :: Maybe (Path Rel)
|
||||||
|
, _dlHash :: Text
|
||||||
|
}
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------
|
||||||
|
--[ Others ]--
|
||||||
|
--------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Where to fetch GHCupDownloads from.
|
||||||
|
data URLSource = GHCupURL
|
||||||
|
| OwnSource URI
|
||||||
|
| OwnSpec GHCupDownloads
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
data Settings = Settings
|
data Settings = Settings
|
||||||
{ cache :: Bool
|
{ cache :: Bool
|
||||||
, urlSource :: URLSource
|
, urlSource :: URLSource
|
||||||
@@ -33,67 +124,11 @@ data DebugInfo = DebugInfo
|
|||||||
|
|
||||||
|
|
||||||
data SetGHC = SetGHCOnly -- ^ unversioned 'ghc'
|
data SetGHC = SetGHCOnly -- ^ unversioned 'ghc'
|
||||||
| SetGHCMajor -- ^ ghc-x.y
|
| SetGHC_XY -- ^ ghc-x.y
|
||||||
| SetGHCMinor -- ^ ghc-x.y.z -- TODO: rename
|
| SetGHC_XYZ -- ^ ghc-x.y.z
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
data Tag = Latest
|
|
||||||
| Recommended
|
|
||||||
deriving (Ord, Eq, Show)
|
|
||||||
|
|
||||||
data VersionInfo = VersionInfo
|
|
||||||
{ _viTags :: [Tag]
|
|
||||||
, _viArch :: ArchitectureSpec
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data DownloadInfo = DownloadInfo
|
|
||||||
{ _dlUri :: URI
|
|
||||||
, _dlSubdir :: Maybe (Path Rel)
|
|
||||||
, _dlHash :: Text
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data Tool = GHC
|
|
||||||
| GHCSrc
|
|
||||||
| Cabal
|
|
||||||
| GHCup
|
|
||||||
deriving (Eq, GHC.Generic, Ord, Show)
|
|
||||||
|
|
||||||
data ToolRequest = ToolRequest
|
|
||||||
{ _trTool :: Tool
|
|
||||||
, _trVersion :: Version
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data Architecture = A_64
|
|
||||||
| A_32
|
|
||||||
deriving (Eq, GHC.Generic, Ord, Show)
|
|
||||||
|
|
||||||
data LinuxDistro = Debian
|
|
||||||
| Ubuntu
|
|
||||||
| Mint
|
|
||||||
| Fedora
|
|
||||||
| CentOS
|
|
||||||
| RedHat
|
|
||||||
| Alpine
|
|
||||||
| AmazonLinux
|
|
||||||
-- rolling
|
|
||||||
| Gentoo
|
|
||||||
| Exherbo
|
|
||||||
-- not known
|
|
||||||
| UnknownLinux
|
|
||||||
-- ^ must exit
|
|
||||||
deriving (Eq, GHC.Generic, Ord, Show)
|
|
||||||
|
|
||||||
data Platform = Linux LinuxDistro
|
|
||||||
-- ^ must exit
|
|
||||||
| Darwin
|
|
||||||
-- ^ must exit
|
|
||||||
| FreeBSD
|
|
||||||
deriving (Eq, GHC.Generic, Ord, Show)
|
|
||||||
|
|
||||||
data PlatformResult = PlatformResult
|
data PlatformResult = PlatformResult
|
||||||
{ _platform :: Platform
|
{ _platform :: Platform
|
||||||
, _distroVersion :: Maybe Versioning
|
, _distroVersion :: Maybe Versioning
|
||||||
@@ -107,21 +142,3 @@ data PlatformRequest = PlatformRequest
|
|||||||
}
|
}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
type PlatformVersionSpec = Map (Maybe Versioning) DownloadInfo
|
|
||||||
type PlatformSpec = Map Platform PlatformVersionSpec
|
|
||||||
type ArchitectureSpec = Map Architecture PlatformSpec
|
|
||||||
type ToolVersionSpec = Map Version VersionInfo
|
|
||||||
type BinaryDownloads = Map Tool ToolVersionSpec
|
|
||||||
|
|
||||||
type SourceDownloads = Map Version DownloadInfo
|
|
||||||
|
|
||||||
data GHCupDownloads = GHCupDownloads {
|
|
||||||
_binaryDownloads :: BinaryDownloads
|
|
||||||
, _sourceDownloads :: SourceDownloads
|
|
||||||
} deriving Show
|
|
||||||
|
|
||||||
data URLSource = GHCupURL
|
|
||||||
| OwnSource URI
|
|
||||||
| OwnSpec GHCupDownloads
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VUnit
|
|||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Tag
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Tag
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupDownloads
|
|
||||||
|
|
||||||
|
|
||||||
instance ToJSON URI where
|
instance ToJSON URI where
|
||||||
|
|||||||
@@ -15,11 +15,9 @@ makePrisms ''Platform
|
|||||||
makePrisms ''Tag
|
makePrisms ''Tag
|
||||||
|
|
||||||
makeLenses ''PlatformResult
|
makeLenses ''PlatformResult
|
||||||
makeLenses ''ToolRequest
|
|
||||||
makeLenses ''DownloadInfo
|
makeLenses ''DownloadInfo
|
||||||
makeLenses ''Tag
|
makeLenses ''Tag
|
||||||
makeLenses ''VersionInfo
|
makeLenses ''VersionInfo
|
||||||
makeLenses ''GHCupDownloads
|
|
||||||
|
|
||||||
|
|
||||||
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
||||||
@@ -45,3 +43,6 @@ hostBSL' = lensVL hostBSL
|
|||||||
|
|
||||||
pathL' :: Lens' (URIRef a) ByteString
|
pathL' :: Lens' (URIRef a) ByteString
|
||||||
pathL' = lensVL pathL
|
pathL' = lensVL pathL
|
||||||
|
|
||||||
|
queryL' :: Lens' (URIRef a) Query
|
||||||
|
queryL' = lensVL queryL
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import Safe
|
import Safe
|
||||||
|
import System.IO.Error
|
||||||
import System.Posix.FilePath ( takeFileName )
|
import System.Posix.FilePath ( takeFileName )
|
||||||
import System.Posix.Files.ByteString ( readSymbolicLink )
|
import System.Posix.Files.ByteString ( readSymbolicLink )
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
@@ -83,6 +84,51 @@ ghcLinkVersion = either (throwM . ParseError) pure . parseOnly parser
|
|||||||
Right r -> pure r
|
Right r -> pure r
|
||||||
|
|
||||||
|
|
||||||
|
-- e.g. ghc-8.6.5
|
||||||
|
rmMinorSymlinks :: (MonadIO m, MonadLogger m) => Version -> m ()
|
||||||
|
rmMinorSymlinks ver = do
|
||||||
|
bindir <- liftIO $ ghcupBinDir
|
||||||
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
|
let myfiles =
|
||||||
|
filter (\x -> ([s|-|] <> verToBS ver) `B.isSuffixOf` toFilePath x) files
|
||||||
|
forM_ myfiles $ \f -> do
|
||||||
|
let fullF = (bindir </> f)
|
||||||
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
|
|
||||||
|
-- E.g. ghc, if this version is the set one.
|
||||||
|
-- This reads `ghcupGHCDir`.
|
||||||
|
rmPlain :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
|
=> Version
|
||||||
|
-> Excepts '[NotInstalled] m ()
|
||||||
|
rmPlain ver = do
|
||||||
|
files <- liftE $ ghcToolFiles ver
|
||||||
|
bindir <- liftIO $ ghcupBinDir
|
||||||
|
forM_ files $ \f -> do
|
||||||
|
let fullF = (bindir </> f)
|
||||||
|
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
|
-- old ghcup
|
||||||
|
let hdc_file = (bindir </> [rel|haddock-ghc|])
|
||||||
|
lift $ $(logDebug) [i|rm -f #{toFilePath hdc_file}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile hdc_file
|
||||||
|
|
||||||
|
-- e.g. ghc-8.6
|
||||||
|
rmMajorSymlinks :: (MonadLogger m, MonadIO m) => Version -> m ()
|
||||||
|
rmMajorSymlinks ver = do
|
||||||
|
(mj, mi) <- liftIO $ getGHCMajor ver
|
||||||
|
let v' = E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi
|
||||||
|
|
||||||
|
bindir <- liftIO ghcupBinDir
|
||||||
|
|
||||||
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
|
let myfiles = filter (\x -> ([s|-|] <> v') `B.isSuffixOf` toFilePath x) files
|
||||||
|
forM_ myfiles $ \f -> do
|
||||||
|
let fullF = (bindir </> f)
|
||||||
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
@@ -90,12 +136,25 @@ ghcLinkVersion = either (throwM . ParseError) pure . parseOnly parser
|
|||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
toolAlreadyInstalled :: Tool -> Version -> IO Bool
|
||||||
|
toolAlreadyInstalled tool ver = case tool of
|
||||||
|
GHC -> ghcInstalled ver
|
||||||
|
Cabal -> cabalInstalled ver
|
||||||
|
GHCup -> pure True
|
||||||
|
|
||||||
|
|
||||||
ghcInstalled :: Version -> IO Bool
|
ghcInstalled :: Version -> IO Bool
|
||||||
ghcInstalled ver = do
|
ghcInstalled ver = do
|
||||||
ghcdir <- ghcupGHCDir ver
|
ghcdir <- ghcupGHCDir ver
|
||||||
doesDirectoryExist ghcdir
|
doesDirectoryExist ghcdir
|
||||||
|
|
||||||
|
|
||||||
|
ghcSrcInstalled :: Version -> IO Bool
|
||||||
|
ghcSrcInstalled ver = do
|
||||||
|
ghcdir <- ghcupGHCDir ver
|
||||||
|
doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
||||||
|
|
||||||
|
|
||||||
ghcSet :: (MonadIO m, MonadThrow m) => m (Maybe Version)
|
ghcSet :: (MonadIO m, MonadThrow m) => m (Maybe Version)
|
||||||
ghcSet = do
|
ghcSet = do
|
||||||
ghcBin <- (</> ([rel|ghc|] :: Path Rel)) <$> liftIO ghcupBinDir
|
ghcBin <- (</> ([rel|ghc|] :: Path Rel)) <$> liftIO ghcupBinDir
|
||||||
@@ -108,10 +167,8 @@ ghcSet = do
|
|||||||
|
|
||||||
cabalInstalled :: Version -> IO Bool
|
cabalInstalled :: Version -> IO Bool
|
||||||
cabalInstalled ver = do
|
cabalInstalled ver = do
|
||||||
cabalbin <- (</> ([rel|cabal|] :: Path Rel)) <$> ghcupBinDir
|
reportedVer <- cabalSet
|
||||||
mc <- executeOut cabalbin [[s|--numeric-version|]] Nothing
|
pure (reportedVer == ver)
|
||||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ mc
|
|
||||||
pure (reportedVer == (verToBS ver))
|
|
||||||
|
|
||||||
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
||||||
cabalSet = do
|
cabalSet = do
|
||||||
@@ -169,7 +226,7 @@ getGHCForMajor major' minor' = do
|
|||||||
unpackToDir :: (MonadLogger m, MonadIO m, MonadThrow m)
|
unpackToDir :: (MonadLogger m, MonadIO m, MonadThrow m)
|
||||||
=> Path Abs -- ^ destination dir
|
=> Path Abs -- ^ destination dir
|
||||||
-> Path Abs -- ^ archive path
|
-> Path Abs -- ^ archive path
|
||||||
-> Excepts '[ArchiveError] m ()
|
-> Excepts '[UnknownArchive] m ()
|
||||||
unpackToDir dest av = do
|
unpackToDir dest av = do
|
||||||
let fp = E.decodeUtf8 (toFilePath av)
|
let fp = E.decodeUtf8 (toFilePath av)
|
||||||
lift $ $(logInfo) [i|Unpacking: #{fp}|]
|
lift $ $(logInfo) [i|Unpacking: #{fp}|]
|
||||||
@@ -198,7 +255,7 @@ unpackToDir dest av = do
|
|||||||
|
|
||||||
|
|
||||||
-- | Get the tool versions that have this tag.
|
-- | Get the tool versions that have this tag.
|
||||||
getTagged :: BinaryDownloads -> Tool -> Tag -> [Version]
|
getTagged :: GHCupDownloads -> Tool -> Tag -> [Version]
|
||||||
getTagged av tool tag = toListOf
|
getTagged av tool tag = toListOf
|
||||||
( ix tool
|
( ix tool
|
||||||
% to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
% to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
||||||
@@ -207,10 +264,10 @@ getTagged av tool tag = toListOf
|
|||||||
)
|
)
|
||||||
av
|
av
|
||||||
|
|
||||||
getLatest :: BinaryDownloads -> Tool -> Maybe Version
|
getLatest :: GHCupDownloads -> Tool -> Maybe Version
|
||||||
getLatest av tool = headOf folded $ getTagged av tool Latest
|
getLatest av tool = headOf folded $ getTagged av tool Latest
|
||||||
|
|
||||||
getRecommended :: BinaryDownloads -> Tool -> Maybe Version
|
getRecommended :: GHCupDownloads -> Tool -> Maybe Version
|
||||||
getRecommended av tool = headOf folded $ getTagged av tool Recommended
|
getRecommended av tool = headOf folded $ getTagged av tool Recommended
|
||||||
|
|
||||||
|
|
||||||
@@ -241,24 +298,33 @@ urlBaseName = parseRel . snd . B.breakEnd (== _slash) . urlDecode False
|
|||||||
|
|
||||||
-- Get tool files from ~/.ghcup/bin/ghc/<ver>/bin/*
|
-- Get tool files from ~/.ghcup/bin/ghc/<ver>/bin/*
|
||||||
-- while ignoring *-<ver> symlinks.
|
-- while ignoring *-<ver> symlinks.
|
||||||
|
--
|
||||||
|
-- Returns unversioned relative files, e.g.:
|
||||||
|
-- ["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]
|
||||||
ghcToolFiles :: (MonadThrow m, MonadFail m, MonadIO m)
|
ghcToolFiles :: (MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> Version
|
||||||
-> Excepts '[NotInstalled] m [Path Rel]
|
-> Excepts '[NotInstalled] m [Path Rel]
|
||||||
ghcToolFiles ver = do
|
ghcToolFiles ver = do
|
||||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||||
|
let bindir = ghcdir </> [rel|bin|]
|
||||||
|
|
||||||
-- fail if ghc is not installed
|
-- fail if ghc is not installed
|
||||||
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
||||||
(throwE (NotInstalled $ ToolRequest GHC ver))
|
(throwE (NotInstalled GHC ver))
|
||||||
|
|
||||||
files <- liftIO $ getDirsFiles' (ghcdir </> ([rel|bin|] :: Path Rel))
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
-- figure out the <ver> suffix, because this might not be `Version` for
|
-- figure out the <ver> suffix, because this might not be `Version` for
|
||||||
-- alpha/rc releases, but x.y.a.somedate.
|
-- alpha/rc releases, but x.y.a.somedate.
|
||||||
(Just symver) <-
|
(Just symver) <-
|
||||||
(B.stripPrefix [s|ghc-|] . takeFileName)
|
(B.stripPrefix [s|ghc-|] . takeFileName)
|
||||||
<$> (liftIO $ readSymbolicLink $ toFilePath
|
<$> (liftIO $ readSymbolicLink $ toFilePath (bindir </> [rel|ghc|]))
|
||||||
(ghcdir </> ([rel|bin/ghc|] :: Path Rel))
|
|
||||||
)
|
|
||||||
when (B.null symver)
|
when (B.null symver)
|
||||||
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
||||||
|
|
||||||
pure $ filter (\x -> not $ symver `B.isSuffixOf` toFilePath x) files
|
pure $ filter (\x -> not $ symver `B.isSuffixOf` toFilePath x) files
|
||||||
|
|
||||||
|
|
||||||
|
-- | This file, when residing in ~/.ghcup/ghc/<ver>/ signals that
|
||||||
|
-- this GHC was built from source. It contains the build config.
|
||||||
|
ghcUpSrcBuiltFile :: Path Rel
|
||||||
|
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ withGHCupTmpDir :: (MonadResource m, MonadThrow m, MonadIO m) => m (Path Abs)
|
|||||||
withGHCupTmpDir = snd <$> allocate mkGhcupTmpDir deleteDirRecursive
|
withGHCupTmpDir = snd <$> allocate mkGhcupTmpDir deleteDirRecursive
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--------------
|
--------------
|
||||||
--[ Others ]--
|
--[ Others ]--
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ execLogged exe spath args lfile chdir env = do
|
|||||||
|
|
||||||
|
|
||||||
SPPB.getProcessStatus True True pid >>= \case
|
SPPB.getProcessStatus True True pid >>= \case
|
||||||
i@(Just (SPPB.Exited es)) -> pure $ toProcessError exe args i
|
i@(Just (SPPB.Exited _)) -> pure $ toProcessError exe args i
|
||||||
i -> pure $ toProcessError exe args i
|
i -> pure $ toProcessError exe args i
|
||||||
|
|
||||||
|
|
||||||
@@ -219,9 +219,9 @@ toProcessError exe args mps = case mps of
|
|||||||
-- | Convert the String to a ByteString with the current
|
-- | Convert the String to a ByteString with the current
|
||||||
-- system encoding.
|
-- system encoding.
|
||||||
unsafePathToString :: Path b -> IO FilePath
|
unsafePathToString :: Path b -> IO FilePath
|
||||||
unsafePathToString (Path p) = do
|
unsafePathToString p = do
|
||||||
enc <- getLocaleEncoding
|
enc <- getLocaleEncoding
|
||||||
unsafeUseAsCStringLen p (peekCStringLen enc)
|
unsafeUseAsCStringLen (toFilePath p) (peekCStringLen enc)
|
||||||
|
|
||||||
|
|
||||||
-- | Search for a file in the search paths.
|
-- | Search for a file in the search paths.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ module GHCup.Utils.Logger where
|
|||||||
|
|
||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO
|
import HPath.IO
|
||||||
@@ -28,15 +29,15 @@ myLoggerT LoggerConfig {..} loggingt = runLoggingT loggingt mylogger
|
|||||||
mylogger _ _ level str' = do
|
mylogger _ _ level str' = do
|
||||||
-- color output
|
-- color output
|
||||||
let l = case level of
|
let l = case level of
|
||||||
LevelDebug -> if lcPrintDebug
|
LevelDebug -> toLogStr (style Bold $ color Blue "[ Debug ]")
|
||||||
then toLogStr (style Bold $ color Blue "[ Debug ]")
|
|
||||||
else mempty
|
|
||||||
LevelInfo -> toLogStr (style Bold $ color Green "[ Info ]")
|
LevelInfo -> toLogStr (style Bold $ color Green "[ Info ]")
|
||||||
LevelWarn -> toLogStr (style Bold $ color Yellow "[ Warn ]")
|
LevelWarn -> toLogStr (style Bold $ color Yellow "[ Warn ]")
|
||||||
LevelError -> toLogStr (style Bold $ color Red "[ Error ]")
|
LevelError -> toLogStr (style Bold $ color Red "[ Error ]")
|
||||||
LevelOther t -> toLogStr "[ " <> toLogStr t <> toLogStr " ]"
|
LevelOther t -> toLogStr "[ " <> toLogStr t <> toLogStr " ]"
|
||||||
let out = fromLogStr (l <> toLogStr " " <> str' <> toLogStr "\n")
|
let out = fromLogStr (l <> toLogStr " " <> str' <> toLogStr "\n")
|
||||||
colorOutter out
|
|
||||||
|
when (lcPrintDebug || (lcPrintDebug == False && not (level == LevelDebug)))
|
||||||
|
$ colorOutter out
|
||||||
|
|
||||||
-- raw output
|
-- raw output
|
||||||
let lr = case level of
|
let lr = case level of
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DeriveLift #-}
|
||||||
{-# LANGUAGE TypeOperators #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE DeriveLift #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE TypeOperators #-}
|
||||||
|
|
||||||
module GHCup.Utils.Prelude where
|
module GHCup.Utils.Prelude where
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ import Data.Versions
|
|||||||
import Haskus.Utils.Types.List
|
import Haskus.Utils.Types.List
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
|
import System.Posix.Env.ByteString ( getEnvironment )
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.Strict.Maybe as S
|
import qualified Data.Strict.Maybe as S
|
||||||
@@ -136,17 +138,17 @@ fromEither :: Either a b -> VEither '[a] b
|
|||||||
fromEither = either (VLeft . V) VRight
|
fromEither = either (VLeft . V) VRight
|
||||||
|
|
||||||
|
|
||||||
liftException :: ( MonadCatch m
|
liftIOException' :: ( MonadCatch m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, Monad m
|
, Monad m
|
||||||
, e :< es'
|
, e :< es'
|
||||||
, LiftVariant es es'
|
, LiftVariant es es'
|
||||||
)
|
)
|
||||||
=> IOErrorType
|
=> IOErrorType
|
||||||
-> e
|
-> e
|
||||||
-> Excepts es m a
|
-> Excepts es m a
|
||||||
-> Excepts es' m a
|
-> Excepts es' m a
|
||||||
liftException errType ex =
|
liftIOException' errType ex =
|
||||||
handleIO
|
handleIO
|
||||||
(\e ->
|
(\e ->
|
||||||
if errType == ioeGetErrorType e then throwE ex else liftIO $ ioError e
|
if errType == ioeGetErrorType e then throwE ex else liftIO $ ioError e
|
||||||
@@ -154,6 +156,19 @@ liftException errType ex =
|
|||||||
. liftE
|
. liftE
|
||||||
|
|
||||||
|
|
||||||
|
liftIOException :: (MonadCatch m, MonadIO m, Monad m, e :< es')
|
||||||
|
=> IOErrorType
|
||||||
|
-> e
|
||||||
|
-> m a
|
||||||
|
-> Excepts es' m a
|
||||||
|
liftIOException errType ex =
|
||||||
|
handleIO
|
||||||
|
(\e ->
|
||||||
|
if errType == ioeGetErrorType e then throwE ex else liftIO $ ioError e
|
||||||
|
)
|
||||||
|
. lift
|
||||||
|
|
||||||
|
|
||||||
hideErrorDef :: IOErrorType -> a -> IO a -> IO a
|
hideErrorDef :: IOErrorType -> a -> IO a -> IO a
|
||||||
hideErrorDef err def =
|
hideErrorDef err def =
|
||||||
handleIO (\e -> if err == ioeGetErrorType e then pure def else ioError e)
|
handleIO (\e -> if err == ioeGetErrorType e then pure def else ioError e)
|
||||||
@@ -174,6 +189,7 @@ hideExcept :: forall e es es' a m
|
|||||||
hideExcept _ a action =
|
hideExcept _ a action =
|
||||||
catchLiftLeft ((\_ -> pure a) :: (e -> Excepts es' m a)) action
|
catchLiftLeft ((\_ -> pure a) :: (e -> Excepts es' m a)) action
|
||||||
|
|
||||||
|
|
||||||
hideExcept' :: forall e es es' m
|
hideExcept' :: forall e es es' m
|
||||||
. (Monad m, e :< es, LiftVariant (Remove e es) es')
|
. (Monad m, e :< es, LiftVariant (Remove e es) es')
|
||||||
=> e
|
=> e
|
||||||
@@ -183,6 +199,23 @@ hideExcept' _ action =
|
|||||||
catchLiftLeft ((\_ -> pure ()) :: (e -> Excepts es' m ())) action
|
catchLiftLeft ((\_ -> pure ()) :: (e -> Excepts es' m ())) action
|
||||||
|
|
||||||
|
|
||||||
|
reThrowAll :: forall e es es' a m
|
||||||
|
. (Monad m, e :< es')
|
||||||
|
=> (V es -> e)
|
||||||
|
-> Excepts es m a
|
||||||
|
-> Excepts es' m a
|
||||||
|
reThrowAll f = catchAllE (throwE . f)
|
||||||
|
|
||||||
|
|
||||||
|
reThrowAllIO :: forall e es es' a m
|
||||||
|
. (MonadCatch m, Monad m, MonadIO m, e :< es')
|
||||||
|
=> (V es -> e)
|
||||||
|
-> (IOException -> e)
|
||||||
|
-> Excepts es m a
|
||||||
|
-> Excepts es' m a
|
||||||
|
reThrowAllIO f g = handleIO (throwE . g) . catchAllE (throwE . f)
|
||||||
|
|
||||||
|
|
||||||
throwEither :: (Exception a, MonadThrow m) => Either a b -> m b
|
throwEither :: (Exception a, MonadThrow m) => Either a b -> m b
|
||||||
throwEither a = case a of
|
throwEither a = case a of
|
||||||
Left e -> throwM e
|
Left e -> throwM e
|
||||||
@@ -200,3 +233,11 @@ intToText = TL.toStrict . B.toLazyText . B.decimal
|
|||||||
removeLensFieldLabel :: String -> String
|
removeLensFieldLabel :: String -> String
|
||||||
removeLensFieldLabel str' =
|
removeLensFieldLabel str' =
|
||||||
maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'
|
maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'
|
||||||
|
|
||||||
|
|
||||||
|
addToCurrentEnv :: MonadIO m
|
||||||
|
=> [(ByteString, ByteString)]
|
||||||
|
-> m [(ByteString, ByteString)]
|
||||||
|
addToCurrentEnv adds = do
|
||||||
|
cEnv <- liftIO $ getEnvironment
|
||||||
|
pure (adds ++ cEnv)
|
||||||
|
|||||||
Reference in New Issue
Block a user