2020-02-28 23:33:32 +00:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
|
|
|
|
|
|
|
|
|
|
|
module Main where
|
|
|
|
|
2020-03-01 00:05:02 +00:00
|
|
|
import GHCup
|
|
|
|
import GHCup.File
|
|
|
|
import GHCup.Logger
|
|
|
|
import GHCup.Prelude
|
|
|
|
import GHCup.Types
|
|
|
|
|
2020-02-28 23:33:32 +00:00
|
|
|
import Control.Monad.Logger
|
|
|
|
import Control.Monad.Reader
|
2020-03-01 01:21:40 +00:00
|
|
|
import Control.Monad.Trans.Resource
|
2020-02-28 23:33:32 +00:00
|
|
|
import Data.Bifunctor
|
|
|
|
import Data.Char
|
|
|
|
import Data.List ( intercalate )
|
|
|
|
import Data.Semigroup ( (<>) )
|
2020-03-01 00:05:02 +00:00
|
|
|
import Data.String.Interpolate
|
2020-02-28 23:33:32 +00:00
|
|
|
import Data.String.QQ
|
|
|
|
import Data.Versions
|
|
|
|
import Haskus.Utils.Variant.Excepts
|
|
|
|
import Options.Applicative hiding ( style )
|
|
|
|
import System.Console.Pretty
|
|
|
|
import System.Exit
|
2020-03-01 00:05:02 +00:00
|
|
|
import System.IO
|
2020-02-28 23:33:32 +00:00
|
|
|
import Text.Layout.Table
|
2020-03-01 00:05:02 +00:00
|
|
|
import URI.ByteString
|
|
|
|
|
|
|
|
import qualified Data.ByteString as B
|
|
|
|
import qualified Data.ByteString.UTF8 as UTF8
|
|
|
|
import qualified Data.Text as T
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data Options = Options
|
2020-03-01 11:54:46 +00:00
|
|
|
{
|
|
|
|
-- global options
|
|
|
|
optVerbose :: Bool
|
2020-02-28 23:33:32 +00:00
|
|
|
, optCache :: Bool
|
|
|
|
, optUrlSource :: Maybe URI
|
2020-03-01 11:54:46 +00:00
|
|
|
-- commands
|
2020-02-28 23:33:32 +00:00
|
|
|
, optCommand :: Command
|
|
|
|
}
|
|
|
|
|
|
|
|
data Command
|
2020-03-01 11:54:46 +00:00
|
|
|
= Install InstallCommand
|
2020-02-28 23:33:32 +00:00
|
|
|
| SetGHC SetGHCOptions
|
|
|
|
| List ListOptions
|
|
|
|
| Rm RmOptions
|
2020-02-29 23:07:39 +00:00
|
|
|
| DInfo
|
|
|
|
|
|
|
|
data ToolVersion = ToolVersion Version
|
|
|
|
| ToolTag Tag
|
|
|
|
|
2020-02-28 23:33:32 +00:00
|
|
|
|
2020-03-01 11:54:46 +00:00
|
|
|
data InstallCommand = InstallGHC InstallOptions
|
|
|
|
| InstallCabal InstallOptions
|
2020-02-28 23:33:32 +00:00
|
|
|
|
2020-03-01 11:54:46 +00:00
|
|
|
data InstallOptions = InstallOptions
|
|
|
|
{ instVer :: Maybe ToolVersion
|
2020-02-28 23:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data SetGHCOptions = SetGHCOptions
|
2020-02-29 23:07:39 +00:00
|
|
|
{ ghcVer :: Maybe ToolVersion
|
2020-02-28 23:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data ListOptions = ListOptions
|
|
|
|
{ lTool :: Maybe Tool
|
|
|
|
, lCriteria :: Maybe ListCriteria
|
|
|
|
}
|
|
|
|
|
|
|
|
data RmOptions = RmOptions
|
|
|
|
{ ghcVer :: Version
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
opts :: Parser Options
|
|
|
|
opts =
|
|
|
|
Options
|
|
|
|
<$> switch
|
|
|
|
(short 'v' <> long "verbose" <> help
|
|
|
|
"Whether to enable verbosity (default: False)"
|
|
|
|
)
|
|
|
|
<*> switch
|
|
|
|
(short 'c' <> long "cache" <> help
|
|
|
|
"Whether to cache downloads (default: False)"
|
|
|
|
)
|
|
|
|
<*> (optional
|
|
|
|
(option
|
|
|
|
(eitherReader parseUri)
|
|
|
|
(short 's' <> long "url-source" <> metavar "URL" <> help
|
|
|
|
"Alternative ghcup download info url (default: internal)"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
<*> com
|
|
|
|
where
|
2020-02-29 23:07:39 +00:00
|
|
|
parseUri s' =
|
|
|
|
bimap show id $ parseURI strictURIParserOptions (UTF8.fromString s')
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
com :: Parser Command
|
2020-03-01 11:54:46 +00:00
|
|
|
com =
|
|
|
|
subparser
|
|
|
|
( command
|
|
|
|
"install"
|
|
|
|
( Install
|
|
|
|
<$> (info (installP <**> helper) (progDesc "Install GHC or cabal"))
|
|
|
|
)
|
|
|
|
<> command
|
|
|
|
"list"
|
|
|
|
( List
|
|
|
|
<$> (info (listOpts <**> helper)
|
|
|
|
(progDesc "Show available GHCs and other tools")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
<> commandGroup "Main commands:"
|
|
|
|
)
|
|
|
|
<|> subparser
|
|
|
|
( command
|
|
|
|
"set"
|
|
|
|
( SetGHC
|
|
|
|
<$> (info (setGHCOpts <**> helper)
|
|
|
|
(progDesc "Set the currently active GHC version")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
<> command
|
|
|
|
"rm"
|
|
|
|
( Rm
|
|
|
|
<$> (info
|
|
|
|
(rmOpts <**> helper)
|
|
|
|
(progDesc "Remove a GHC version installed by ghcup")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
<> commandGroup "GHC commands:"
|
|
|
|
<> hidden
|
|
|
|
)
|
|
|
|
<|> subparser
|
|
|
|
( command
|
|
|
|
"debug-info"
|
|
|
|
((\_ -> DInfo) <$> (info (helper) (progDesc "Show debug info")))
|
|
|
|
<> commandGroup "Other commands:"
|
|
|
|
<> hidden
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
installP :: Parser InstallCommand
|
|
|
|
installP = subparser
|
2020-02-28 23:33:32 +00:00
|
|
|
( command
|
2020-03-01 11:54:46 +00:00
|
|
|
"ghc"
|
2020-02-28 23:33:32 +00:00
|
|
|
( InstallGHC
|
2020-03-01 11:54:46 +00:00
|
|
|
<$> (info (installOpts <**> helper) (progDesc "Install a GHC version"))
|
2020-02-28 23:33:32 +00:00
|
|
|
)
|
|
|
|
<> command
|
2020-03-01 11:54:46 +00:00
|
|
|
"cabal"
|
2020-02-28 23:33:32 +00:00
|
|
|
( InstallCabal
|
2020-03-01 11:54:46 +00:00
|
|
|
<$> (info (installOpts <**> helper)
|
|
|
|
(progDesc "Install or update a Cabal version")
|
2020-02-28 23:33:32 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-03-01 11:54:46 +00:00
|
|
|
installOpts :: Parser InstallOptions
|
|
|
|
installOpts = InstallOptions <$> optional toolVersionParser
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
setGHCOpts :: Parser SetGHCOptions
|
2020-02-29 23:07:39 +00:00
|
|
|
setGHCOpts = SetGHCOptions <$> optional toolVersionParser
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
listOpts :: Parser ListOptions
|
|
|
|
listOpts =
|
|
|
|
ListOptions
|
|
|
|
<$> optional
|
|
|
|
(option
|
|
|
|
(eitherReader toolParser)
|
|
|
|
(short 't' <> long "tool" <> metavar "<ghc|cabal>" <> help
|
2020-02-29 23:07:39 +00:00
|
|
|
"Tool to list versions for. Default is all"
|
2020-02-28 23:33:32 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
<*> (optional
|
|
|
|
(option
|
|
|
|
(eitherReader criteriaParser)
|
|
|
|
( short 'c'
|
|
|
|
<> long "show-criteria"
|
|
|
|
<> metavar "<installed|set>"
|
|
|
|
<> help "Show only installed or set tool versions"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
rmOpts :: Parser RmOptions
|
2020-02-29 23:07:39 +00:00
|
|
|
rmOpts =
|
|
|
|
RmOptions
|
|
|
|
<$> (option
|
|
|
|
(eitherReader
|
|
|
|
(bimap (const "Not a valid version") id . version . T.pack)
|
|
|
|
)
|
|
|
|
(short 'v' <> long "version" <> metavar "VERSION" <> help
|
|
|
|
"The GHC version to remove"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-02-28 23:33:32 +00:00
|
|
|
|
2020-02-29 23:07:39 +00:00
|
|
|
versionParser :: Parser Version
|
|
|
|
versionParser = option
|
|
|
|
(eitherReader (bimap (const "Not a valid version") id . version . T.pack))
|
2020-03-01 11:54:46 +00:00
|
|
|
(short 'v' <> long "version" <> metavar "VERSION" <> help "The target version"
|
|
|
|
)
|
2020-02-29 23:07:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
toolVersionParser :: Parser ToolVersion
|
|
|
|
toolVersionParser = verP <|> toolP
|
|
|
|
where
|
|
|
|
verP = ToolVersion <$> versionParser
|
|
|
|
toolP =
|
|
|
|
ToolTag
|
|
|
|
<$> (option
|
|
|
|
(eitherReader
|
|
|
|
(\s' -> case fmap toLower s' of
|
|
|
|
"recommended" -> Right Recommended
|
|
|
|
"latest" -> Right Latest
|
|
|
|
other -> Left ([i|Unknown tag #{other}|])
|
|
|
|
)
|
|
|
|
)
|
2020-03-01 11:54:46 +00:00
|
|
|
(short 't' <> long "tag" <> metavar "TAG" <> help "The target tag")
|
2020-02-29 23:07:39 +00:00
|
|
|
)
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
toolParser :: String -> Either String Tool
|
2020-02-29 23:07:39 +00:00
|
|
|
toolParser s' | t == T.pack "ghc" = Right GHC
|
|
|
|
| t == T.pack "cabal" = Right Cabal
|
|
|
|
| otherwise = Left ("Unknown tool: " <> s')
|
|
|
|
where t = T.toLower (T.pack s')
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
criteriaParser :: String -> Either String ListCriteria
|
2020-02-29 23:07:39 +00:00
|
|
|
criteriaParser s' | t == T.pack "installed" = Right ListInstalled
|
|
|
|
| t == T.pack "set" = Right ListSet
|
|
|
|
| otherwise = Left ("Unknown criteria: " <> s')
|
|
|
|
where t = T.toLower (T.pack s')
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
toSettings :: Options -> Settings
|
|
|
|
toSettings Options {..} =
|
|
|
|
let cache = optCache
|
|
|
|
urlSource = maybe GHCupURL OwnSource optUrlSource
|
|
|
|
in Settings { .. }
|
|
|
|
|
|
|
|
|
|
|
|
-- TODO: something better than Show instance for errors
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
|
|
|
|
|
|
|
customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
|
|
|
|
>>= \opt@Options {..} -> do
|
2020-03-01 00:05:02 +00:00
|
|
|
let settings = toSettings opt
|
|
|
|
|
|
|
|
-- logger interpreter
|
|
|
|
let runLogger = myLoggerT (LoggerConfig optVerbose $ B.hPut stderr)
|
|
|
|
|
2020-02-28 23:33:32 +00:00
|
|
|
-- wrapper to run effects with settings
|
|
|
|
let runInstTool =
|
|
|
|
runLogger
|
|
|
|
. flip runReaderT settings
|
2020-03-01 01:21:40 +00:00
|
|
|
. runResourceT
|
2020-02-28 23:33:32 +00:00
|
|
|
. runE
|
2020-02-29 23:07:39 +00:00
|
|
|
@'[ AlreadyInstalled
|
2020-02-28 23:33:32 +00:00
|
|
|
, ArchiveError
|
|
|
|
, DistroNotFound
|
2020-02-29 23:07:39 +00:00
|
|
|
, FileDoesNotExistError
|
|
|
|
, FileError
|
2020-02-28 23:33:32 +00:00
|
|
|
, JSONError
|
2020-02-29 23:07:39 +00:00
|
|
|
, NoCompatibleArch
|
|
|
|
, NoDownload
|
|
|
|
, NotInstalled
|
|
|
|
, PlatformResultError
|
|
|
|
, ProcessError
|
|
|
|
, TagNotFound
|
|
|
|
, URLException
|
2020-02-28 23:33:32 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
let runSetGHC =
|
|
|
|
runLogger
|
|
|
|
. flip runReaderT settings
|
2020-02-29 23:07:39 +00:00
|
|
|
. runE
|
|
|
|
@'[ FileDoesNotExistError
|
|
|
|
, NotInstalled
|
|
|
|
, TagNotFound
|
|
|
|
, URLException
|
|
|
|
, JSONError
|
|
|
|
, TagNotFound
|
|
|
|
]
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
let runListGHC =
|
|
|
|
runLogger
|
|
|
|
. flip runReaderT settings
|
2020-02-29 23:07:39 +00:00
|
|
|
. runE @'[FileDoesNotExistError , URLException , JSONError]
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
let runRmGHC =
|
2020-02-29 23:07:39 +00:00
|
|
|
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
|
|
|
|
|
|
|
let runDebugInfo =
|
2020-02-28 23:33:32 +00:00
|
|
|
runLogger
|
|
|
|
. flip runReaderT settings
|
2020-02-29 23:07:39 +00:00
|
|
|
. runE
|
|
|
|
@'[PlatformResultError , NoCompatibleArch , DistroNotFound]
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
case optCommand of
|
2020-03-01 11:54:46 +00:00
|
|
|
Install (InstallGHC InstallOptions {..}) ->
|
2020-02-28 23:33:32 +00:00
|
|
|
void
|
|
|
|
$ (runInstTool $ do
|
|
|
|
av <- liftE getDownloads
|
2020-03-01 11:54:46 +00:00
|
|
|
v <- liftE $ fromVersion av instVer GHC
|
2020-02-29 23:07:39 +00:00
|
|
|
liftE $ installTool (ToolRequest GHC v) Nothing
|
2020-02-28 23:33:32 +00:00
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight _ -> runLogger
|
|
|
|
$ $(logInfo) ([s|GHC installation successful|])
|
|
|
|
VLeft (V (AlreadyInstalled treq)) ->
|
|
|
|
runLogger $ $(logWarn)
|
|
|
|
(T.pack (show treq) <> [s| already installed|])
|
2020-02-29 23:07:39 +00:00
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-03-01 11:54:46 +00:00
|
|
|
Install (InstallGHC InstallOptions {..}) ->
|
2020-02-28 23:33:32 +00:00
|
|
|
void
|
|
|
|
$ (runInstTool $ do
|
|
|
|
av <- liftE getDownloads
|
2020-03-01 11:54:46 +00:00
|
|
|
v <- liftE $ fromVersion av instVer Cabal
|
2020-02-29 23:07:39 +00:00
|
|
|
liftE $ installTool (ToolRequest Cabal v) Nothing
|
2020-02-28 23:33:32 +00:00
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight _ -> runLogger
|
|
|
|
$ $(logInfo) ([s|Cabal installation successful|])
|
|
|
|
VLeft (V (AlreadyInstalled treq)) ->
|
|
|
|
runLogger $ $(logWarn)
|
|
|
|
(T.pack (show treq) <> [s| already installed|])
|
2020-02-29 23:07:39 +00:00
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
SetGHC (SetGHCOptions {..}) ->
|
|
|
|
void
|
|
|
|
$ (runSetGHC $ do
|
|
|
|
av <- liftE getDownloads
|
2020-02-29 23:07:39 +00:00
|
|
|
v <- liftE $ fromVersion av ghcVer GHC
|
2020-02-28 23:33:32 +00:00
|
|
|
liftE $ setGHC v SetGHCOnly
|
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight _ ->
|
|
|
|
runLogger $ $(logInfo) ([s|GHC successfully set|])
|
2020-02-29 23:07:39 +00:00
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
List (ListOptions {..}) ->
|
|
|
|
void
|
|
|
|
$ (runListGHC $ do
|
|
|
|
liftE $ listVersions lTool lCriteria
|
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight r -> liftIO $ printListResult r
|
2020-02-29 23:07:39 +00:00
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-02-28 23:33:32 +00:00
|
|
|
|
|
|
|
Rm (RmOptions {..}) ->
|
|
|
|
void
|
|
|
|
$ (runRmGHC $ do
|
|
|
|
liftE $ rmGHCVer ghcVer
|
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight _ -> pure ()
|
2020-02-29 23:07:39 +00:00
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-02-28 23:33:32 +00:00
|
|
|
|
2020-02-29 23:07:39 +00:00
|
|
|
DInfo -> do
|
|
|
|
void
|
|
|
|
$ (runDebugInfo $ do
|
|
|
|
liftE $ getDebugInfo
|
|
|
|
)
|
|
|
|
>>= \case
|
|
|
|
VRight dinfo -> putStrLn $ show dinfo
|
|
|
|
VLeft e ->
|
|
|
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
2020-02-28 23:33:32 +00:00
|
|
|
pure ()
|
|
|
|
|
|
|
|
|
2020-02-29 23:07:39 +00:00
|
|
|
fromVersion :: Monad m
|
|
|
|
=> AvailableDownloads
|
|
|
|
-> Maybe ToolVersion
|
|
|
|
-> Tool
|
|
|
|
-> Excepts '[TagNotFound] m Version
|
|
|
|
fromVersion av Nothing tool =
|
|
|
|
getRecommended av tool ?? TagNotFound Recommended tool
|
|
|
|
fromVersion _ (Just (ToolVersion v)) _ = pure v
|
|
|
|
fromVersion av (Just (ToolTag Latest)) tool =
|
|
|
|
getLatest av tool ?? TagNotFound Latest tool
|
|
|
|
fromVersion av (Just (ToolTag Recommended)) tool =
|
|
|
|
getRecommended av tool ?? TagNotFound Recommended tool
|
|
|
|
|
|
|
|
|
2020-02-28 23:33:32 +00:00
|
|
|
printListResult :: [ListResult] -> IO ()
|
|
|
|
printListResult lr = do
|
|
|
|
let
|
|
|
|
formatted =
|
|
|
|
gridString
|
|
|
|
[ column expand left def def
|
|
|
|
, column expand left def def
|
|
|
|
, column expand left def def
|
|
|
|
, column expand left def def
|
|
|
|
]
|
|
|
|
$ fmap
|
|
|
|
(\ListResult {..} ->
|
|
|
|
[ if
|
|
|
|
| lSet -> (color Green "✔✔")
|
|
|
|
| lInstalled -> (color Green "✓")
|
|
|
|
| otherwise -> (color Red "✗")
|
|
|
|
, fmap toLower . show $ lTool
|
|
|
|
, T.unpack . prettyVer $ lVer
|
|
|
|
, intercalate "," $ ((fmap . fmap) toLower . fmap show $ lTag)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
lr
|
|
|
|
putStrLn $ formatted
|