ghcup-hs/app/ghcup/Main.hs

933 lines
30 KiB
Haskell
Raw Normal View History

2020-04-09 17:53:22 +00:00
{-# LANGUAGE CPP #-}
2020-01-11 20:15:05 +00:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
2020-03-21 21:19:37 +00:00
{-# LANGUAGE OverloadedStrings #-}
2020-01-11 20:15:05 +00:00
{-# LANGUAGE DuplicateRecordFields #-}
module Main where
import GHCup
import GHCup.Download
import GHCup.Errors
2020-04-10 15:36:27 +00:00
import GHCup.Platform
import GHCup.Requirements
2020-01-11 20:15:05 +00:00
import GHCup.Types
import GHCup.Utils
2020-04-17 14:56:56 +00:00
import GHCup.Utils.File
2020-01-11 20:15:05 +00:00
import GHCup.Utils.Logger
import GHCup.Utils.Prelude
import GHCup.Version
2020-04-09 17:53:22 +00:00
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
2020-01-11 20:15:05 +00:00
import Control.Monad.Logger
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import Data.Bifunctor
import Data.Char
2020-03-09 21:21:22 +00:00
import Data.Either
2020-03-17 00:58:59 +00:00
import Data.Functor
2020-01-11 20:15:05 +00:00
import Data.List ( intercalate )
import Data.String.Interpolate
2020-03-17 00:58:59 +00:00
import Data.Text ( Text )
2020-01-11 20:15:05 +00:00
import Data.Versions
2020-03-17 00:58:59 +00:00
import Data.Void
import GHC.IO.Encoding
2020-01-11 20:15:05 +00:00
import Haskus.Utils.Variant.Excepts
import HPath
import HPath.IO
2020-04-17 14:56:56 +00:00
import Language.Haskell.TH
2020-01-11 20:15:05 +00:00
import Options.Applicative hiding ( style )
import Prelude hiding ( appendFile )
import System.Console.Pretty
import System.Environment
import System.Exit
import System.IO hiding ( appendFile )
import Text.Read
import Text.Layout.Table
import URI.ByteString
import qualified Data.ByteString as B
import qualified Data.ByteString.UTF8 as UTF8
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Encoding as E
2020-03-17 00:58:59 +00:00
import qualified Text.Megaparsec as MP
2020-01-11 20:15:05 +00:00
data Options = Options
{
-- global options
optVerbose :: Bool
, optCache :: Bool
, optUrlSource :: Maybe URI
, optNoVerify :: Bool
-- commands
, optCommand :: Command
}
data Command
= Install InstallOptions
| InstallCabal InstallOptions
2020-01-11 20:15:05 +00:00
| SetGHC SetGHCOptions
| List ListOptions
| Rm RmOptions
| DInfo
| Compile CompileCommand
| Upgrade UpgradeOpts Bool
2020-04-10 15:36:27 +00:00
| ToolRequirements
2020-01-11 20:15:05 +00:00
data ToolVersion = ToolVersion Version
| ToolTag Tag
data InstallOptions = InstallOptions
{ instVer :: Maybe ToolVersion
, instPlatform :: Maybe PlatformRequest
2020-01-11 20:15:05 +00:00
}
data SetGHCOptions = SetGHCOptions
{ ghcVer :: Maybe ToolVersion
}
data ListOptions = ListOptions
{ lTool :: Maybe Tool
, lCriteria :: Maybe ListCriteria
}
data RmOptions = RmOptions
{ ghcVer :: Version
}
data CompileCommand = CompileGHC CompileOptions
| CompileCabal CompileOptions
data CompileOptions = CompileOptions
{ targetVer :: Version
, bootstrapGhc :: Either Version (Path Abs)
2020-01-11 20:15:05 +00:00
, jobs :: Maybe Int
, buildConfig :: Maybe (Path Abs)
, patchDir :: Maybe (Path Abs)
2020-01-11 20:15:05 +00:00
}
data UpgradeOpts = UpgradeInplace
| UpgradeAt (Path Abs)
| UpgradeGHCupDir
deriving Show
opts :: Parser Options
opts =
Options
<$> switch
(short 'v' <> long "verbose" <> help
2020-04-12 10:22:27 +00:00
"Enable verbosity"
2020-01-11 20:15:05 +00:00
)
<*> switch
(short 'c' <> long "cache" <> help
2020-04-12 10:22:27 +00:00
"Cache downloads in ~/.ghcup/cache"
2020-01-11 20:15:05 +00:00
)
<*> (optional
(option
(eitherReader parseUri)
2020-03-09 21:21:22 +00:00
( short 's'
<> long "url-source"
<> metavar "URL"
<> help "Alternative ghcup download info url"
<> internal
2020-01-11 20:15:05 +00:00
)
)
)
<*> switch
(short 'n' <> long "no-verify" <> help
2020-04-12 10:22:27 +00:00
"Skip tarball checksum verification"
2020-01-11 20:15:05 +00:00
)
<*> com
where
parseUri s' =
bimap show id $ parseURI strictURIParserOptions (UTF8.fromString s')
com :: Parser Command
com =
subparser
( command
"install"
((info ((Install <$> installOpts) <**> helper)
(progDesc "Install or update GHC")
)
2020-01-11 20:15:05 +00:00
)
<> command
"set"
( SetGHC
<$> (info (setGHCOpts <**> helper)
(progDesc "Set currently active GHC version")
)
)
<> command
"rm"
( Rm
<$> (info (rmOpts <**> helper) (progDesc "Remove a GHC version"))
)
<> command
"install-cabal"
((info ((InstallCabal <$> installOpts) <**> helper)
(progDesc "Install or update cabal")
)
)
2020-01-11 20:15:05 +00:00
<> command
"list"
( List
<$> (info (listOpts <**> helper)
(progDesc "Show available GHCs and other tools")
)
)
<> command
"upgrade"
(info ((Upgrade <$> upgradeOptsP <*>
switch
(short 'f' <> long "force" <> help
"Force update"
)
) <**> helper) (progDesc "Upgrade ghcup"))
2020-03-09 21:21:22 +00:00
<> command
"compile"
( Compile
<$> (info (compileP <**> helper)
(progDesc "Compile a tool from source")
2020-01-11 20:15:05 +00:00
)
2020-03-09 21:21:22 +00:00
)
2020-01-11 20:15:05 +00:00
<> commandGroup "Main commands:"
)
<|> subparser
( command
"debug-info"
((\_ -> DInfo) <$> (info (helper) (progDesc "Show debug info")))
2020-04-10 15:36:27 +00:00
<> command
"tool-requirements"
( (\_ -> ToolRequirements)
<$> (info (helper)
(progDesc "Show the requirements for ghc/cabal")
)
2020-04-10 15:36:27 +00:00
)
2020-01-11 20:15:05 +00:00
<> commandGroup "Other commands:"
<> hidden
)
installOpts :: Parser InstallOptions
installOpts =
(flip InstallOptions)
<$> (optional
(option
(eitherReader platformParser)
( short 'p'
<> long "platform"
<> metavar "PLATFORM"
<> help
"Override for platform (triple matching ghc tarball names), e.g. x86_64-fedora27-linux"
)
)
)
<*> optional toolVersionArgument
2020-01-11 20:15:05 +00:00
setGHCOpts :: Parser SetGHCOptions
setGHCOpts = SetGHCOptions <$> optional toolVersionArgument
2020-01-11 20:15:05 +00:00
listOpts :: Parser ListOptions
listOpts =
ListOptions
<$> optional
(option
(eitherReader toolParser)
(short 't' <> long "tool" <> metavar "<ghc|cabal>" <> help
"Tool to list versions for. Default is all"
)
)
<*> (optional
(option
(eitherReader criteriaParser)
( short 'c'
<> long "show-criteria"
<> metavar "<installed|set>"
<> help "Show only installed or set tool versions"
)
)
)
rmOpts :: Parser RmOptions
rmOpts = RmOptions <$> versionArgument
2020-01-11 20:15:05 +00:00
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 =
CompileOptions
<$> (option
(eitherReader
(bimap (const "Not a valid version") id . version . T.pack)
)
(short 'v' <> long "version" <> metavar "VERSION" <> help
"The tool version to compile"
)
)
<*> (option
(eitherReader
(\x ->
(bimap (const "Not a valid version") Left . version . T.pack $ x)
<|> (bimap show Right . parseAbs . E.encodeUtf8 . T.pack $ x)
)
2020-01-11 20:15:05 +00:00
)
( short 'b'
<> long "bootstrap-ghc"
<> metavar "BOOTSTRAP_GHC"
<> help
"The GHC version (or full path) to bootstrap with (must be installed)"
2020-01-11 20:15:05 +00:00
)
)
<*> optional
(option
(eitherReader (readEither @Int))
(short 'j' <> long "jobs" <> metavar "JOBS" <> help
"How many jobs to use for make"
)
)
<*> optional
(option
(eitherReader
(\x ->
bimap show id . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
String
(Path Abs)
)
)
(short 'c' <> long "config" <> metavar "CONFIG" <> help
"Absolute path to build config file"
)
)
<*> optional
(option
(eitherReader
(\x ->
bimap show id . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
String
(Path Abs)
)
)
(short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
"Absolute path to patch directory (applied in order, uses -p1)"
)
)
2020-01-11 20:15:05 +00:00
toolVersionParser :: Parser ToolVersion
toolVersionParser = verP <|> toolP
where
verP = ToolVersion <$> versionParser
toolP =
ToolTag
<$> (option
(eitherReader tagEither)
2020-01-11 20:15:05 +00:00
(short 't' <> long "tag" <> metavar "TAG" <> help "The target tag")
)
-- | same as toolVersionParser, except as an argument.
toolVersionArgument :: Parser ToolVersion
toolVersionArgument =
argument (eitherReader toolVersionEither) (metavar "VERSION|TAG")
versionArgument :: Parser Version
versionArgument = argument
(eitherReader versionEither)
(metavar "VERSION")
versionParser :: Parser Version
versionParser = option
(eitherReader versionEither)
(short 'v' <> long "version" <> metavar "VERSION" <> help "The target version"
)
tagEither :: String -> Either String Tag
tagEither s' = case fmap toLower s' of
"recommended" -> Right Recommended
"latest" -> Right Latest
other -> Left ([i|Unknown tag #{other}|])
versionEither :: String -> Either String Version
versionEither s' =
-- 'version' is a bit too lax and will parse typoed tags
case readMaybe ((:[]) . head $ s') :: Maybe Int of
Just _ -> bimap (const "Not a valid version") id . version . T.pack $ s'
Nothing -> Left "Not a valid version"
toolVersionEither :: String -> Either String ToolVersion
toolVersionEither s' =
bimap id ToolTag (tagEither s') <|> bimap id ToolVersion (versionEither s')
2020-01-11 20:15:05 +00:00
toolParser :: String -> Either String Tool
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')
criteriaParser :: String -> Either String ListCriteria
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-03-17 00:58:59 +00:00
platformParser :: String -> Either String PlatformRequest
platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
Right r -> pure r
Left e -> Left $ errorBundlePretty e
where
archP :: MP.Parsec Void Text Architecture
2020-03-21 21:19:37 +00:00
archP = (MP.try (MP.chunk "x86_64" $> A_64)) <|> (MP.chunk "i386" $> A_32)
2020-03-17 00:58:59 +00:00
platformP :: MP.Parsec Void Text PlatformRequest
platformP = choice'
[ (\a mv -> PlatformRequest a FreeBSD mv)
2020-03-21 21:19:37 +00:00
<$> (archP <* MP.chunk "-")
<*> ( MP.chunk "portbld"
*> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof))
2020-03-17 00:58:59 +00:00
<|> pure Nothing
)
2020-03-21 21:19:37 +00:00
<* MP.chunk "-freebsd"
2020-03-17 00:58:59 +00:00
)
, (\a mv -> PlatformRequest a Darwin mv)
2020-03-21 21:19:37 +00:00
<$> (archP <* MP.chunk "-")
<*> ( MP.chunk "apple"
*> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof))
2020-03-17 00:58:59 +00:00
<|> pure Nothing
)
2020-03-21 21:19:37 +00:00
<* MP.chunk "-darwin"
2020-03-17 00:58:59 +00:00
)
, (\a d mv -> PlatformRequest a (Linux d) mv)
2020-03-21 21:19:37 +00:00
<$> (archP <* MP.chunk "-")
2020-03-17 00:58:59 +00:00
<*> distroP
2020-03-21 21:19:37 +00:00
<*> ((MP.try (Just <$> verP (MP.chunk "-linux" <* MP.eof)) <|> pure Nothing
)
<* MP.chunk "-linux"
2020-03-17 00:58:59 +00:00
)
]
distroP :: MP.Parsec Void Text LinuxDistro
distroP = choice'
2020-03-21 21:19:37 +00:00
[ MP.chunk "debian" $> Debian
, MP.chunk "deb" $> Debian
, MP.chunk "ubuntu" $> Ubuntu
, MP.chunk "mint" $> Mint
, MP.chunk "fedora" $> Fedora
, MP.chunk "centos" $> CentOS
, MP.chunk "redhat" $> RedHat
, MP.chunk "alpine" $> Alpine
, MP.chunk "gentoo" $> Gentoo
, MP.chunk "exherbo" $> Exherbo
, MP.chunk "unknown" $> UnknownLinux
2020-03-17 00:58:59 +00:00
]
verP :: MP.Parsec Void Text Text -> MP.Parsec Void Text Versioning
verP suffix = do
ver <- parseUntil suffix
if T.null ver
then fail "empty version"
else do
rest <- MP.getInput
MP.setInput ver
v <- versioning'
MP.setInput rest
pure v
choice' [] = fail "Empty list"
choice' [x ] = x
choice' (x : xs) = MP.try x <|> choice' xs
parseUntil :: MP.Parsec Void Text Text -> MP.Parsec Void Text Text
parseUntil p = do
(MP.try (MP.lookAhead p) $> mempty)
<|> (do
c <- T.singleton <$> MP.anySingle
c2 <- parseUntil p
pure (c `mappend` c2)
)
2020-01-11 20:15:05 +00:00
toSettings :: Options -> Settings
toSettings Options {..} =
2020-03-17 17:39:01 +00:00
let cache = optCache
noVerify = optNoVerify
2020-01-11 20:15:05 +00:00
in Settings { .. }
upgradeOptsP :: Parser UpgradeOpts
upgradeOptsP =
flag'
UpgradeInplace
(short 'i' <> long "inplace" <> help
"Upgrade ghcup in-place (wherever it's at)"
)
<|> ( UpgradeAt
<$> (option
(eitherReader
(\x ->
bimap show id . parseAbs . E.encodeUtf8 . T.pack $ x :: Either
String
(Path Abs)
)
)
(short 't' <> long "target" <> metavar "TARGET_DIR" <> help
"Absolute filepath to write ghcup into"
)
)
)
<|> (pure UpgradeGHCupDir)
2020-04-17 14:56:56 +00:00
describe_result :: String
describe_result = $( (LitE . StringL) <$>
runIO (do
CapturedProcess{..} <- executeOut [rel|git|] ["describe"] Nothing
case _exitCode of
ExitSuccess -> pure . T.unpack . decUTF8Safe $ _stdOut
ExitFailure _ -> pure numericVer
)
)
2020-01-11 20:15:05 +00:00
main :: IO ()
main = do
2020-04-17 14:56:56 +00:00
let
versionHelp = infoOption
(("The GHCup Haskell installer, version " <>
)
$ (head . lines $ describe_result)
)
(long "version" <> help "Show version")
let numericVersionHelp = infoOption
numericVer
( long "numeric-version"
<> help "Show the numeric version (for use in scripts)"
)
2020-01-11 20:15:05 +00:00
2020-04-17 14:56:56 +00:00
customExecParser
(prefs showHelpOnError)
(info (opts <**> helper <**> versionHelp <**> numericVersionHelp) idm)
2020-01-11 20:15:05 +00:00
>>= \opt@Options {..} -> do
let settings = toSettings opt
2020-03-17 18:16:21 +00:00
-- create ~/.ghcup dir
ghcdir <- ghcupBaseDir
createDirIfMissing newDirPerms ghcdir
2020-01-11 20:15:05 +00:00
-- logger interpreter
2020-03-16 09:47:09 +00:00
logfile <- initGHCupFileLogging [rel|ghcup.log|]
2020-01-11 20:15:05 +00:00
let runLogger = myLoggerT LoggerConfig
{ lcPrintDebug = optVerbose
, colorOutter = B.hPut stderr
, rawOutter = appendFile logfile
}
-- wrapper to run effects with settings
let runInstTool =
runLogger
. flip runReaderT settings
. runResourceT
. runE
@'[ AlreadyInstalled
, UnknownArchive
, DistroNotFound
, FileDoesNotExistError
, CopyError
, NoCompatibleArch
, NoDownload
, NotInstalled
, NoCompatiblePlatform
, BuildFailed
, TagNotFound
, DigestError
, DownloadFailed
]
2020-03-09 21:21:22 +00:00
let
runSetGHC =
runLogger
. flip runReaderT settings
. runE
@'[ FileDoesNotExistError
, NotInstalled
, TagNotFound
, TagNotFound
]
2020-01-11 20:15:05 +00:00
let runListGHC =
runLogger
. flip runReaderT settings
2020-03-09 21:21:22 +00:00
. runE @'[FileDoesNotExistError]
2020-01-11 20:15:05 +00:00
let runRmGHC =
runLogger . flip runReaderT settings . runE @'[NotInstalled]
let runDebugInfo =
runLogger
. flip runReaderT settings
. runE
@'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
let runCompileGHC =
runLogger
. flip runReaderT settings
. runResourceT
. runE
@'[ AlreadyInstalled
, BuildFailed
, DigestError
2020-04-10 17:27:17 +00:00
, DistroNotFound
, DownloadFailed
2020-01-11 20:15:05 +00:00
, GHCupSetError
2020-04-10 17:27:17 +00:00
, NoCompatibleArch
, NoCompatiblePlatform
2020-01-11 20:15:05 +00:00
, NoDownload
2020-04-10 20:44:43 +00:00
, NotFoundInPATH
, PatchFailed
2020-01-11 20:15:05 +00:00
, UnknownArchive
]
let runCompileCabal =
runLogger
. flip runReaderT settings
. runResourceT
. runE
2020-04-10 17:27:17 +00:00
@'[ BuildFailed
2020-01-11 20:15:05 +00:00
, DigestError
2020-04-10 17:27:17 +00:00
, DistroNotFound
2020-03-09 21:21:22 +00:00
, DownloadFailed
2020-04-10 17:27:17 +00:00
, NoCompatibleArch
, NoCompatiblePlatform
, NoDownload
, PatchFailed
, UnknownArchive
2020-01-11 20:15:05 +00:00
]
let runUpgrade =
runLogger
. flip runReaderT settings
. runResourceT
. runE
@'[ DigestError
, DistroNotFound
, NoCompatiblePlatform
, NoCompatibleArch
, NoDownload
, NoUpdate
2020-01-11 20:15:05 +00:00
, FileDoesNotExistError
, CopyError
2020-03-09 21:21:22 +00:00
, DownloadFailed
2020-01-11 20:15:05 +00:00
]
2020-04-10 15:36:27 +00:00
(GHCupInfo treq dls) <-
2020-03-09 21:21:22 +00:00
( runLogger
. flip runReaderT settings
. runE @'[JSONError , DownloadFailed]
2020-03-17 17:39:01 +00:00
$ liftE
$ getDownloads (maybe GHCupURL OwnSource optUrlSource)
2020-03-09 21:21:22 +00:00
)
>>= \case
VRight r -> pure r
2020-04-17 16:26:55 +00:00
VLeft e -> do
2020-03-09 21:21:22 +00:00
runLogger
($(logError) [i|Error fetching download info: #{e}|])
2020-04-17 16:26:55 +00:00
exitWith (ExitFailure 2)
2020-03-09 21:21:22 +00:00
runLogger $ checkForUpdates dls
2020-01-11 20:15:05 +00:00
2020-04-17 16:26:55 +00:00
res <- case optCommand of
Install (InstallOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runInstTool $ do
2020-03-09 21:21:22 +00:00
v <- liftE $ fromVersion dls instVer GHC
liftE $ installGHCBin dls v instPlatform
2020-01-11 20:15:05 +00:00
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ -> do
2020-03-21 21:19:37 +00:00
runLogger $ $(logInfo) ("GHC installation successful")
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft (V (AlreadyInstalled _ v)) -> do
2020-01-11 20:15:05 +00:00
runLogger $ $(logWarn)
[i|GHC ver #{prettyVer v} already installed|]
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft (V (BuildFailed tmpdir e)) -> do
2020-01-11 20:15:05 +00:00
runLogger
($(logError) [i|Build failed with #{e}
2020-03-17 17:39:41 +00:00
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
2020-01-11 20:15:05 +00:00
)
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 3
2020-01-11 20:15:05 +00:00
VLeft e -> do
runLogger $ do
$(logError) [i|#{e}|]
$(logError) [i|Also check the logs in ~/.ghcup/logs|]
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 3
InstallCabal (InstallOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runInstTool $ do
2020-03-09 21:21:22 +00:00
v <- liftE $ fromVersion dls instVer Cabal
liftE $ installCabalBin dls v instPlatform
2020-01-11 20:15:05 +00:00
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ -> do
2020-03-21 21:19:37 +00:00
runLogger $ $(logInfo) ("Cabal installation successful")
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft (V (AlreadyInstalled _ v)) -> do
2020-01-11 20:15:05 +00:00
runLogger $ $(logWarn)
[i|Cabal ver #{prettyVer v} already installed|]
2020-04-17 16:26:55 +00:00
pure ExitSuccess
2020-01-11 20:15:05 +00:00
VLeft e -> do
runLogger $ do
$(logError) [i|#{e}|]
$(logError) [i|Also check the logs in ~/.ghcup/logs|]
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 4
2020-01-11 20:15:05 +00:00
SetGHC (SetGHCOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runSetGHC $ do
2020-03-09 21:21:22 +00:00
v <- liftE $ fromVersion dls ghcVer GHC
2020-01-11 20:15:05 +00:00
liftE $ setGHC v SetGHCOnly
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight v -> do
runLogger $ $(logInfo) [i|GHC #{prettyVer v} successfully set as default version|]
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 5
2020-01-11 20:15:05 +00:00
List (ListOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runListGHC $ do
2020-01-11 20:15:05 +00:00
liftIO $ listVersions dls lTool lCriteria
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight r -> do
liftIO $ printListResult r
pure ExitSuccess
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 6
2020-01-11 20:15:05 +00:00
Rm (RmOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runRmGHC $ do
2020-01-11 20:15:05 +00:00
liftE $ rmGHCVer ghcVer
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ ->
pure ExitSuccess
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 7
2020-01-11 20:15:05 +00:00
DInfo -> do
2020-04-17 16:26:55 +00:00
(runDebugInfo $ do
2020-01-11 20:15:05 +00:00
liftE $ getDebugInfo
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight dinfo -> do
putStrLn $ prettyDebugInfo dinfo
pure ExitSuccess
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 8
2020-01-11 20:15:05 +00:00
Compile (CompileGHC CompileOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runCompileGHC $ do
2020-01-11 20:15:05 +00:00
liftE
$ compileGHC dls targetVer bootstrapGhc jobs buildConfig patchDir
2020-01-11 20:15:05 +00:00
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ -> do
2020-01-11 20:15:05 +00:00
runLogger $ $(logInfo)
2020-03-21 21:19:37 +00:00
("GHC successfully compiled and installed")
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft (V (AlreadyInstalled _ v)) -> do
2020-01-11 20:15:05 +00:00
runLogger $ $(logWarn)
[i|GHC ver #{prettyVer v} already installed|]
2020-04-17 16:26:55 +00:00
pure ExitSuccess
VLeft (V (BuildFailed tmpdir e)) -> do
2020-01-11 20:15:05 +00:00
runLogger
($(logError) [i|Build failed with #{e}
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
Make sure to clean up #{tmpdir} afterwards.|]
2020-01-11 20:15:05 +00:00
)
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 9
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 9
2020-01-11 20:15:05 +00:00
Compile (CompileCabal CompileOptions {..}) ->
2020-04-17 16:26:55 +00:00
(runCompileCabal $ do
liftE $ compileCabal dls targetVer bootstrapGhc jobs patchDir
2020-01-11 20:15:05 +00:00
)
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ -> do
runLogger ($(logInfo)
"Cabal successfully compiled and installed")
pure ExitSuccess
VLeft (V (BuildFailed tmpdir e)) -> do
2020-01-11 20:15:05 +00:00
runLogger
($(logError) [i|Build failed with #{e}
2020-03-17 17:39:41 +00:00
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
2020-01-11 20:15:05 +00:00
)
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 10
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 10
2020-01-11 20:15:05 +00:00
Upgrade (uOpts) force -> do
2020-01-11 20:15:05 +00:00
target <- case uOpts of
UpgradeInplace -> do
efp <- liftIO $ getExecutablePath
p <- parseAbs . E.encodeUtf8 . T.pack $ efp
pure $ Just p
(UpgradeAt p) -> pure $ Just p
UpgradeGHCupDir -> do
bdir <- liftIO $ ghcupBinDir
2020-03-16 09:47:09 +00:00
pure (Just (bdir </> [rel|ghcup|]))
2020-01-11 20:15:05 +00:00
2020-04-17 16:26:55 +00:00
(runUpgrade $ (liftE $ upgradeGHCup dls target force))
>>= \case
VRight v' -> do
let pretty_v = prettyVer v'
runLogger
$ $(logInfo)
[i|Successfully upgraded GHCup to version #{pretty_v}|]
pure ExitSuccess
VLeft (V NoUpdate) -> do
runLogger $ $(logWarn)
[i|No GHCup update available|]
pure ExitSuccess
VLeft e -> do
runLogger ($(logError) [i|#{e}|])
pure $ ExitFailure 11
2020-01-11 20:15:05 +00:00
2020-04-10 15:36:27 +00:00
ToolRequirements -> (runLogger $ runE
@'[ NoCompatiblePlatform
, DistroNotFound
, NoToolRequirements
] $ do
platform <- liftE $ getPlatform
req <- (getCommonRequirements platform $ treq)
?? NoToolRequirements
liftIO $ T.hPutStr stdout (prettyRequirements req))
>>= \case
2020-04-17 16:26:55 +00:00
VRight _ -> pure ExitSuccess
VLeft e -> do
2020-04-10 15:36:27 +00:00
runLogger
($(logError) [i|Error getting tool requirements: #{e}|])
2020-04-17 16:26:55 +00:00
pure $ ExitFailure 12
case res of
ExitSuccess -> pure ()
ef@(ExitFailure _) -> do
runLogger ($(logError) [i|If you think this is a bug, report at: https://gitlab.haskell.org/haskell/ghcup-hs/issues|])
exitWith ef
2020-01-11 20:15:05 +00:00
pure ()
fromVersion :: Monad m
=> GHCupDownloads
-> 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
printListResult :: [ListResult] -> IO ()
printListResult lr = do
-- https://gitlab.haskell.org/ghc/ghc/issues/8118
setLocaleEncoding utf8
2020-01-11 20:15:05 +00:00
let
formatted =
gridString
[ 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
(\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)
, if fromSrc then (color Blue "compiled") else mempty
]
)
lr
putStrLn $ formatted
2020-03-09 21:21:22 +00:00
checkForUpdates :: (MonadFail m, MonadLogger m) => GHCupDownloads -> m ()
checkForUpdates dls = do
forM_ (getLatest dls GHCup) $ \l -> do
(Right ghc_ver) <- pure $ version $ prettyPVP ghcUpVer
when (l > ghc_ver)
$ $(logWarn)
[i|New GHCup version available: #{prettyVer l}. To upgrade, run 'ghcup upgrade'|]
2020-04-17 15:12:59 +00:00
prettyDebugInfo :: DebugInfo -> String
prettyDebugInfo DebugInfo {..} = [i|Debug Info
==========
GHCup base dir: #{toFilePath diBaseDir}
GHCup bin dir: #{toFilePath diBinDir}
GHCup GHC directory: #{toFilePath diGHCDir}
GHCup cache directory: #{toFilePath diCacheDir}
Architecture: #{prettyArch diArch}
Platform: #{prettyPlatform diPlatform}
Version: #{describe_result}|]
where
prettyArch :: Architecture -> String
prettyArch A_64 = "amd64"
prettyArch A_32 = "i386"
prettyPlatform :: PlatformResult -> String
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Just v' }
= show plat <> ", " <> show v'
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Nothing }
= show plat
2020-04-17 16:26:55 +00:00