Compare commits
27 Commits
a93aaf9a5f
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
| 1810bb27a8 | |||
| 3ff6be5435 | |||
| 0963081fd8 | |||
| af42598a27 | |||
| e6037b9eb5 | |||
| e58e1c1954 | |||
| c7a831a280 | |||
| e77ed1a26c | |||
| c0c70f5c9b | |||
| fee16758de | |||
| f8448cf02b | |||
| 35b6359c1b | |||
| 9c7d17800d | |||
| ee570c024c | |||
| fcb7129251 | |||
| 8a1bd45ffe | |||
| f5a2db6719 | |||
| 2c99070d89 | |||
| 93aac16fc5 | |||
| 775c541895 | |||
| b0eba1a77a | |||
| 8aa2be5898 | |||
| 951a7173ae | |||
| b7f49b1c94 | |||
| dcd6812fb7 | |||
| 167826dfce | |||
| 03ee8915fb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
dist-newstyle/
|
dist-newstyle/
|
||||||
|
.stack-work/
|
||||||
|
|||||||
157
README.md
157
README.md
@@ -1,37 +1,144 @@
|
|||||||
# ghcup
|
`ghcup` makes it easy to install specific versions of `ghc` on GNU/Linux,
|
||||||
|
macOS (aka Darwin) and FreeBSD and can also bootstrap a fresh Haskell developer environment from scratch.
|
||||||
|
It follows the unix UNIX philosophy of [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well).
|
||||||
|
|
||||||
A rewrite of ghcup in haskell.
|
Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [pyenv](https://github.com/pyenv/pyenv) and [jenv](http://www.jenv.be).
|
||||||
|
|
||||||
## TODO
|
*Ubuntu users may prefer [hvr's ppa](https://launchpad.net/~hvr/+archive/ubuntu/ghc).*
|
||||||
|
|
||||||
* create static ghcup binaries
|
## Table of Contents
|
||||||
* adjust url in GHCupDownloads
|
|
||||||
* add print-system-reqs command
|
|
||||||
|
|
||||||
## Motivation
|
* [Installation](#installation)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Manpages](#manpages)
|
||||||
|
* [Design goals](#design-goals)
|
||||||
|
* [How](#how)
|
||||||
|
* [Known users](#known-users)
|
||||||
|
* [Known problems](#known-problems)
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
Maintenance problems:
|
## Installation
|
||||||
|
|
||||||
* platform incompatibilities regularly causing breaking bugs:
|
### Simple bootstrap
|
||||||
* [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)
|
|
||||||
* [Wrong tar flag on darwin](https://gitlab.haskell.org/haskell/ghcup/issues/119))
|
|
||||||
* refactoring being difficult due to POSIX sh
|
|
||||||
|
|
||||||
Benefits of a rewrite:
|
Follow the instructions at [https://www.haskell.org/ghcup/](https://www.haskell.org/ghcup/)
|
||||||
|
|
||||||
* 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
|
### Manual install
|
||||||
* Refactoring will be easier
|
|
||||||
* Better tool support (such as linting the downloads file)
|
|
||||||
* saner downloads file format (such as JSON)
|
|
||||||
|
|
||||||
Downsides:
|
Download the binary for your platform at [https://github.com/hasufell/ghcup-hs/releases](https://github.com/hasufell/ghcup-hs/releases)
|
||||||
|
and place it into your `PATH` anywhere.
|
||||||
|
|
||||||
* building static binaries for all platforms (and possibly causing SSL/DNS problems)
|
Then adjust your `PATH` in `~/.bashrc` (or similar, depending on your shell) like so:
|
||||||
* still bootstrapping those binaries via a POSIX sh script
|
|
||||||
|
|
||||||
## Goals
|
```sh
|
||||||
|
export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See `ghcup --help`.
|
||||||
|
|
||||||
|
Common use cases are:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# list available ghc/cabal versions
|
||||||
|
ghcup list
|
||||||
|
|
||||||
|
# install the recommended GHC version
|
||||||
|
ghcup install ghc
|
||||||
|
|
||||||
|
# install a specific GHC version
|
||||||
|
ghcup install ghc -v 8.2.2
|
||||||
|
|
||||||
|
# set the currently "active" GHC version
|
||||||
|
ghcup set -v 8.4.4
|
||||||
|
|
||||||
|
# install cabal-install
|
||||||
|
ghcup install cabal
|
||||||
|
|
||||||
|
# update ghcup itself
|
||||||
|
ghcup upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
Generally this is meant to be used with [`cabal-install`](https://hackage.haskell.org/package/cabal-install), which
|
||||||
|
handles your haskell packages and can demand that [a specific version](https://cabal.readthedocs.io/en/latest/nix-local-build.html#cfg-flag---with-compiler) of `ghc` is available, which `ghcup` can do.
|
||||||
|
|
||||||
|
### Manpages
|
||||||
|
|
||||||
|
For man pages to work you need [man-db](http://man-db.nongnu.org/) as your `man` provider, then issue `man ghc`. Manpages only work for the currently set ghc.
|
||||||
|
`MANPATH` may be required to be unset.
|
||||||
|
|
||||||
|
## Design goals
|
||||||
|
|
||||||
|
1. simplicity
|
||||||
|
2. non-interactive
|
||||||
|
3. portable (eh)
|
||||||
|
4. do one thing and do it well (UNIX philosophy)
|
||||||
|
|
||||||
|
### Non-goals
|
||||||
|
|
||||||
|
1. invoking `sudo`, `apt-get` or *any* package manager
|
||||||
|
2. handling system packages
|
||||||
|
3. handling cabal projects
|
||||||
|
4. being a stack alternative
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
Installs a specified GHC version into `~/.ghcup/ghc/<ver>`, and places `ghc-<ver>` symlinks in `~/.ghcup/bin/`.
|
||||||
|
|
||||||
|
Optionally, an unversioned `ghc` link can point to a default version of your choice.
|
||||||
|
|
||||||
|
This uses precompiled GHC binaries that have been compiled on fedora/debian by [upstream GHC](https://www.haskell.org/ghc/download_ghc_8_6_1.html#binaries).
|
||||||
|
|
||||||
|
Alternatively, you can also tell it to compile from source (note that this might fail due to missing requirements).
|
||||||
|
|
||||||
|
In addition this script can also install `cabal-install`.
|
||||||
|
|
||||||
|
## Known users
|
||||||
|
|
||||||
|
* [vabal](https://github.com/Franciman/vabal)
|
||||||
|
|
||||||
|
## Known problems
|
||||||
|
|
||||||
|
### Limited distributions supported
|
||||||
|
|
||||||
|
Currently only GNU/Linux distributions compatible with the [upstream GHC](https://www.haskell.org/ghc/download_ghc_8_6_1.html#binaries) binaries are supported.
|
||||||
|
|
||||||
|
### Precompiled binaries
|
||||||
|
|
||||||
|
Since this uses precompiled binaries you may run into
|
||||||
|
several problems.
|
||||||
|
|
||||||
|
#### Missing libtinfo (ncurses)
|
||||||
|
|
||||||
|
You may run into problems with *ncurses* and **missing libtinfo**, in case
|
||||||
|
your distribution doesn't use the legacy way of building
|
||||||
|
ncurses and has no compatibility symlinks in place.
|
||||||
|
|
||||||
|
Ask your distributor on how to solve this or
|
||||||
|
try to compile from source via `ghcup compile <version>`.
|
||||||
|
|
||||||
|
#### Libnuma required
|
||||||
|
|
||||||
|
This was a [bug](https://ghc.haskell.org/trac/ghc/ticket/15688) in the build system of some GHC versions that lead to
|
||||||
|
unconditionally enabled libnuma support. To mitigate this you might have to install the libnuma
|
||||||
|
package of your distribution. See [here](https://gitlab.haskell.org/haskell/ghcup/issues/58) for a discussion.
|
||||||
|
|
||||||
|
### Compilation
|
||||||
|
|
||||||
|
Although this script can compile GHC for you, it's just a very thin
|
||||||
|
wrapper around the build system. It makes no effort in trying
|
||||||
|
to figure out whether you have the correct toolchain and
|
||||||
|
the correct dependencies. Refer to [the official docs](https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/Linux)
|
||||||
|
on how to prepare your environment for building GHC.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
1. Why reimplement stack?
|
||||||
|
|
||||||
|
ghcup is not a reimplementation of stack. The only common part is automatic installation of GHC, but even that differs in scope and design.
|
||||||
|
|
||||||
|
2. Why not support windows?
|
||||||
|
|
||||||
|
Consider using [Chocolatey](https://chocolatey.org/search?q=ghc) or [ghcups](https://github.com/kakkun61/ghcups).
|
||||||
|
|
||||||
* Correct low-level code
|
|
||||||
* Good exception handling
|
|
||||||
* Cleaner user interface
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -161,7 +161,7 @@ validateTarballs dls = do
|
|||||||
|
|
||||||
where
|
where
|
||||||
downloadAll dli = do
|
downloadAll dli = do
|
||||||
let settings = Settings True GHCupURL False
|
let settings = Settings True False
|
||||||
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
||||||
, colorOutter = B.hPut stderr
|
, colorOutter = B.hPut stderr
|
||||||
, rawOutter = (\_ -> pure ())
|
, rawOutter = (\_ -> pure ())
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
|
|
||||||
|
|
||||||
@@ -15,18 +16,23 @@ import GHCup.Types
|
|||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
import GHCup.Version
|
import GHCup.Version
|
||||||
|
|
||||||
|
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.Resource
|
import Control.Monad.Trans.Resource
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
import Data.Either
|
||||||
|
import Data.Functor
|
||||||
import Data.List ( intercalate )
|
import Data.List ( intercalate )
|
||||||
import Data.Semigroup ( (<>) )
|
import Data.Semigroup ( (<>) )
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
|
import Data.Void
|
||||||
|
import GHC.IO.Encoding
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO
|
import HPath.IO
|
||||||
@@ -45,6 +51,7 @@ 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.IO as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
|
import qualified Text.Megaparsec as MP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -79,7 +86,8 @@ data InstallCommand = InstallGHC InstallOptions
|
|||||||
| InstallCabal InstallOptions
|
| InstallCabal InstallOptions
|
||||||
|
|
||||||
data InstallOptions = InstallOptions
|
data InstallOptions = InstallOptions
|
||||||
{ instVer :: Maybe ToolVersion
|
{ instVer :: Maybe ToolVersion
|
||||||
|
, instPlatform :: Maybe PlatformRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
data SetGHCOptions = SetGHCOptions
|
data SetGHCOptions = SetGHCOptions
|
||||||
@@ -127,8 +135,11 @@ opts =
|
|||||||
<*> (optional
|
<*> (optional
|
||||||
(option
|
(option
|
||||||
(eitherReader parseUri)
|
(eitherReader parseUri)
|
||||||
(short 's' <> long "url-source" <> metavar "URL" <> help
|
( short 's'
|
||||||
"Alternative ghcup download info url" <> internal
|
<> long "url-source"
|
||||||
|
<> metavar "URL"
|
||||||
|
<> help "Alternative ghcup download info url"
|
||||||
|
<> internal
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -167,13 +178,13 @@ com =
|
|||||||
(progDesc "Upgrade ghcup (per default in ~/.ghcup/bin/)")
|
(progDesc "Upgrade ghcup (per default in ~/.ghcup/bin/)")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<> command
|
<> command
|
||||||
"compile"
|
"compile"
|
||||||
( Compile
|
( Compile
|
||||||
<$> (info (compileP <**> helper)
|
<$> (info (compileP <**> helper)
|
||||||
(progDesc "Compile a tool from source")
|
(progDesc "Compile a tool from source")
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
<> commandGroup "Main commands:"
|
<> commandGroup "Main commands:"
|
||||||
)
|
)
|
||||||
<|> subparser
|
<|> subparser
|
||||||
@@ -226,7 +237,21 @@ installP = subparser
|
|||||||
)
|
)
|
||||||
|
|
||||||
installOpts :: Parser InstallOptions
|
installOpts :: Parser InstallOptions
|
||||||
installOpts = InstallOptions <$> optional toolVersionParser
|
installOpts =
|
||||||
|
InstallOptions
|
||||||
|
<$> optional toolVersionParser
|
||||||
|
<*> (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"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
setGHCOpts :: Parser SetGHCOptions
|
setGHCOpts :: Parser SetGHCOptions
|
||||||
setGHCOpts = SetGHCOptions <$> optional toolVersionParser
|
setGHCOpts = SetGHCOptions <$> optional toolVersionParser
|
||||||
@@ -356,11 +381,83 @@ criteriaParser s' | t == T.pack "installed" = Right ListInstalled
|
|||||||
where t = T.toLower (T.pack s')
|
where t = T.toLower (T.pack s')
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
archP = (MP.try (MP.chunk "x86_64" $> A_64)) <|> (MP.chunk "i386" $> A_32)
|
||||||
|
platformP :: MP.Parsec Void Text PlatformRequest
|
||||||
|
platformP = choice'
|
||||||
|
[ (\a mv -> PlatformRequest a FreeBSD mv)
|
||||||
|
<$> (archP <* MP.chunk "-")
|
||||||
|
<*> ( MP.chunk "portbld"
|
||||||
|
*> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof))
|
||||||
|
<|> pure Nothing
|
||||||
|
)
|
||||||
|
<* MP.chunk "-freebsd"
|
||||||
|
)
|
||||||
|
, (\a mv -> PlatformRequest a Darwin mv)
|
||||||
|
<$> (archP <* MP.chunk "-")
|
||||||
|
<*> ( MP.chunk "apple"
|
||||||
|
*> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof))
|
||||||
|
<|> pure Nothing
|
||||||
|
)
|
||||||
|
<* MP.chunk "-darwin"
|
||||||
|
)
|
||||||
|
, (\a d mv -> PlatformRequest a (Linux d) mv)
|
||||||
|
<$> (archP <* MP.chunk "-")
|
||||||
|
<*> distroP
|
||||||
|
<*> ((MP.try (Just <$> verP (MP.chunk "-linux" <* MP.eof)) <|> pure Nothing
|
||||||
|
)
|
||||||
|
<* MP.chunk "-linux"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
distroP :: MP.Parsec Void Text LinuxDistro
|
||||||
|
distroP = choice'
|
||||||
|
[ 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
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
toSettings :: Options -> Settings
|
toSettings :: Options -> Settings
|
||||||
toSettings Options {..} =
|
toSettings Options {..} =
|
||||||
let cache = optCache
|
let cache = optCache
|
||||||
urlSource = maybe GHCupURL OwnSource optUrlSource
|
noVerify = optNoVerify
|
||||||
noVerify = optNoVerify
|
|
||||||
in Settings { .. }
|
in Settings { .. }
|
||||||
|
|
||||||
|
|
||||||
@@ -397,8 +494,12 @@ main = do
|
|||||||
>>= \opt@Options {..} -> do
|
>>= \opt@Options {..} -> do
|
||||||
let settings = toSettings opt
|
let settings = toSettings opt
|
||||||
|
|
||||||
|
-- create ~/.ghcup dir
|
||||||
|
ghcdir <- ghcupBaseDir
|
||||||
|
createDirIfMissing newDirPerms ghcdir
|
||||||
|
|
||||||
-- logger interpreter
|
-- logger interpreter
|
||||||
logfile <- initGHCupFileLogging ([rel|ghcup.log|] :: Path Rel)
|
logfile <- initGHCupFileLogging [rel|ghcup.log|]
|
||||||
let runLogger = myLoggerT LoggerConfig
|
let runLogger = myLoggerT LoggerConfig
|
||||||
{ lcPrintDebug = optVerbose
|
{ lcPrintDebug = optVerbose
|
||||||
, colorOutter = B.hPut stderr
|
, colorOutter = B.hPut stderr
|
||||||
@@ -416,7 +517,6 @@ main = do
|
|||||||
, DistroNotFound
|
, DistroNotFound
|
||||||
, FileDoesNotExistError
|
, FileDoesNotExistError
|
||||||
, CopyError
|
, CopyError
|
||||||
, JSONError
|
|
||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, NotInstalled
|
, NotInstalled
|
||||||
@@ -427,22 +527,21 @@ main = do
|
|||||||
, DownloadFailed
|
, DownloadFailed
|
||||||
]
|
]
|
||||||
|
|
||||||
let runSetGHC =
|
let
|
||||||
runLogger
|
runSetGHC =
|
||||||
. flip runReaderT settings
|
runLogger
|
||||||
. runE
|
. flip runReaderT settings
|
||||||
@'[ FileDoesNotExistError
|
. runE
|
||||||
, NotInstalled
|
@'[ FileDoesNotExistError
|
||||||
, TagNotFound
|
, NotInstalled
|
||||||
, JSONError
|
, TagNotFound
|
||||||
, TagNotFound
|
, TagNotFound
|
||||||
, DownloadFailed
|
]
|
||||||
]
|
|
||||||
|
|
||||||
let runListGHC =
|
let runListGHC =
|
||||||
runLogger
|
runLogger
|
||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runE @'[FileDoesNotExistError , JSONError , DownloadFailed]
|
. runE @'[FileDoesNotExistError]
|
||||||
|
|
||||||
let runRmGHC =
|
let runRmGHC =
|
||||||
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
||||||
@@ -461,12 +560,10 @@ main = do
|
|||||||
@'[ AlreadyInstalled
|
@'[ AlreadyInstalled
|
||||||
, BuildFailed
|
, BuildFailed
|
||||||
, DigestError
|
, DigestError
|
||||||
, DownloadFailed
|
|
||||||
, GHCupSetError
|
, GHCupSetError
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, UnknownArchive
|
, UnknownArchive
|
||||||
--
|
, DownloadFailed
|
||||||
, JSONError
|
|
||||||
]
|
]
|
||||||
|
|
||||||
let runCompileCabal =
|
let runCompileCabal =
|
||||||
@@ -474,12 +571,11 @@ main = do
|
|||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runResourceT
|
. runResourceT
|
||||||
. runE
|
. runE
|
||||||
@'[ JSONError
|
@'[ UnknownArchive
|
||||||
, UnknownArchive
|
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, DigestError
|
, DigestError
|
||||||
, DownloadFailed
|
|
||||||
, BuildFailed
|
, BuildFailed
|
||||||
|
, DownloadFailed
|
||||||
]
|
]
|
||||||
|
|
||||||
let runUpgrade =
|
let runUpgrade =
|
||||||
@@ -493,30 +589,42 @@ main = do
|
|||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoDownload
|
, NoDownload
|
||||||
, FileDoesNotExistError
|
, FileDoesNotExistError
|
||||||
, JSONError
|
|
||||||
, DownloadFailed
|
|
||||||
, CopyError
|
, CopyError
|
||||||
|
, DownloadFailed
|
||||||
]
|
]
|
||||||
|
|
||||||
|
dls <-
|
||||||
|
( runLogger
|
||||||
|
. flip runReaderT settings
|
||||||
|
. runE @'[JSONError , DownloadFailed]
|
||||||
|
$ liftE
|
||||||
|
$ getDownloads (maybe GHCupURL OwnSource optUrlSource)
|
||||||
|
)
|
||||||
|
>>= \case
|
||||||
|
VRight r -> pure r
|
||||||
|
VLeft e ->
|
||||||
|
runLogger
|
||||||
|
($(logError) [i|Error fetching download info: #{e}|])
|
||||||
|
>> exitFailure
|
||||||
|
runLogger $ checkForUpdates dls
|
||||||
|
|
||||||
case optCommand of
|
case optCommand of
|
||||||
Install (InstallGHC InstallOptions {..}) ->
|
Install (InstallGHC InstallOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runInstTool $ do
|
$ (runInstTool $ do
|
||||||
dls <- liftE getDownloads
|
v <- liftE $ fromVersion dls instVer GHC
|
||||||
v <- liftE $ fromVersion dls instVer GHC
|
liftE $ installGHCBin dls v instPlatform
|
||||||
liftE $ installGHCBin dls v Nothing
|
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> runLogger
|
VRight _ ->
|
||||||
$ $(logInfo) ([s|GHC installation successful|])
|
runLogger $ $(logInfo) ("GHC installation successful")
|
||||||
VLeft (V (AlreadyInstalled _ v)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
[i|GHC ver #{prettyVer v} already installed|]
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
VLeft (V (BuildFailed tmpdir e)) ->
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
runLogger
|
runLogger
|
||||||
($(logError) [i|Build failed with #{e}
|
($(logError) [i|Build failed with #{e}
|
||||||
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
)
|
)
|
||||||
>> exitFailure
|
>> exitFailure
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
@@ -527,13 +635,12 @@ Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.
|
|||||||
Install (InstallCabal InstallOptions {..}) ->
|
Install (InstallCabal InstallOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runInstTool $ do
|
$ (runInstTool $ do
|
||||||
dls <- liftE getDownloads
|
v <- liftE $ fromVersion dls instVer Cabal
|
||||||
v <- liftE $ fromVersion dls instVer Cabal
|
liftE $ installCabalBin dls v instPlatform
|
||||||
liftE $ installCabalBin dls v Nothing
|
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> runLogger
|
VRight _ ->
|
||||||
$ $(logInfo) ([s|Cabal installation successful|])
|
runLogger $ $(logInfo) ("Cabal installation successful")
|
||||||
VLeft (V (AlreadyInstalled _ v)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
[i|Cabal ver #{prettyVer v} already installed|]
|
[i|Cabal ver #{prettyVer v} already installed|]
|
||||||
@@ -546,20 +653,18 @@ Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.
|
|||||||
SetGHC (SetGHCOptions {..}) ->
|
SetGHC (SetGHCOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runSetGHC $ do
|
$ (runSetGHC $ do
|
||||||
dls <- liftE getDownloads
|
v <- liftE $ fromVersion dls ghcVer GHC
|
||||||
v <- liftE $ fromVersion dls ghcVer GHC
|
|
||||||
liftE $ setGHC v SetGHCOnly
|
liftE $ setGHC v SetGHCOnly
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ ->
|
VRight _ ->
|
||||||
runLogger $ $(logInfo) ([s|GHC successfully set|])
|
runLogger $ $(logInfo) ("GHC successfully set")
|
||||||
VLeft e ->
|
VLeft e ->
|
||||||
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
runLogger ($(logError) [i|#{e}|]) >> exitFailure
|
||||||
|
|
||||||
List (ListOptions {..}) ->
|
List (ListOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runListGHC $ do
|
$ (runListGHC $ do
|
||||||
dls <- liftE getDownloads
|
|
||||||
liftIO $ listVersions dls lTool lCriteria
|
liftIO $ listVersions dls lTool lCriteria
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
@@ -590,21 +695,20 @@ Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.
|
|||||||
Compile (CompileGHC CompileOptions {..}) ->
|
Compile (CompileGHC CompileOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runCompileGHC $ do
|
$ (runCompileGHC $ do
|
||||||
dls <- liftE getDownloads
|
|
||||||
liftE
|
liftE
|
||||||
$ compileGHC dls targetVer bootstrapVer jobs buildConfig
|
$ compileGHC dls targetVer bootstrapVer jobs buildConfig
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ ->
|
VRight _ ->
|
||||||
runLogger $ $(logInfo)
|
runLogger $ $(logInfo)
|
||||||
([s|GHC successfully compiled and installed|])
|
("GHC successfully compiled and installed")
|
||||||
VLeft (V (AlreadyInstalled _ v)) ->
|
VLeft (V (AlreadyInstalled _ v)) ->
|
||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
[i|GHC ver #{prettyVer v} already installed|]
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
VLeft (V (BuildFailed tmpdir e)) ->
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
runLogger
|
runLogger
|
||||||
($(logError) [i|Build failed with #{e}
|
($(logError) [i|Build failed with #{e}
|
||||||
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
)
|
)
|
||||||
>> exitFailure
|
>> exitFailure
|
||||||
VLeft e ->
|
VLeft e ->
|
||||||
@@ -613,20 +717,16 @@ Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.
|
|||||||
Compile (CompileCabal CompileOptions {..}) ->
|
Compile (CompileCabal CompileOptions {..}) ->
|
||||||
void
|
void
|
||||||
$ (runCompileCabal $ do
|
$ (runCompileCabal $ do
|
||||||
dls <- liftE getDownloads
|
liftE $ compileCabal dls targetVer bootstrapVer jobs
|
||||||
liftE $ compileCabal dls
|
|
||||||
targetVer
|
|
||||||
bootstrapVer
|
|
||||||
jobs
|
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ ->
|
VRight _ ->
|
||||||
runLogger $ $(logInfo)
|
runLogger $ $(logInfo)
|
||||||
([s|Cabal successfully compiled and installed|])
|
("Cabal successfully compiled and installed")
|
||||||
VLeft (V (BuildFailed tmpdir e)) ->
|
VLeft (V (BuildFailed tmpdir e)) ->
|
||||||
runLogger
|
runLogger
|
||||||
($(logError) [i|Build failed with #{e}
|
($(logError) [i|Build failed with #{e}
|
||||||
Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
||||||
)
|
)
|
||||||
>> exitFailure
|
>> exitFailure
|
||||||
VLeft e ->
|
VLeft e ->
|
||||||
@@ -641,11 +741,10 @@ Check the logs at ~/ghcup/logs and the build directory #{tmpdir} for more clues.
|
|||||||
(UpgradeAt p) -> pure $ Just p
|
(UpgradeAt p) -> pure $ Just p
|
||||||
UpgradeGHCupDir -> do
|
UpgradeGHCupDir -> do
|
||||||
bdir <- liftIO $ ghcupBinDir
|
bdir <- liftIO $ ghcupBinDir
|
||||||
pure (Just (bdir </> ([rel|ghcup|] :: Path Rel)))
|
pure (Just (bdir </> [rel|ghcup|]))
|
||||||
|
|
||||||
void
|
void
|
||||||
$ (runUpgrade $ do
|
$ (runUpgrade $ do
|
||||||
dls <- liftE getDownloads
|
|
||||||
liftE $ upgradeGHCup dls target
|
liftE $ upgradeGHCup dls target
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
@@ -677,6 +776,9 @@ fromVersion av (Just (ToolTag Recommended)) tool =
|
|||||||
|
|
||||||
printListResult :: [ListResult] -> IO ()
|
printListResult :: [ListResult] -> IO ()
|
||||||
printListResult lr = do
|
printListResult lr = do
|
||||||
|
-- https://gitlab.haskell.org/ghc/ghc/issues/8118
|
||||||
|
setLocaleEncoding utf8
|
||||||
|
|
||||||
let
|
let
|
||||||
formatted =
|
formatted =
|
||||||
gridString
|
gridString
|
||||||
@@ -700,3 +802,12 @@ printListResult lr = do
|
|||||||
)
|
)
|
||||||
lr
|
lr
|
||||||
putStrLn $ formatted
|
putStrLn $ formatted
|
||||||
|
|
||||||
|
|
||||||
|
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'|]
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ package ghcup
|
|||||||
package tar-bytestring
|
package tar-bytestring
|
||||||
ghc-options: -O2
|
ghc-options: -O2
|
||||||
|
|
||||||
|
constraints: http-io-streams -brotli
|
||||||
|
|
||||||
|
index-state: 2020-03-09T18:53:34Z
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
constraints: any.Cabal ==2.4.0.1,
|
constraints: any.Cabal ==2.4.0.1,
|
||||||
|
any.Glob ==0.10.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,
|
||||||
|
any.Only ==0.1,
|
||||||
any.QuickCheck ==2.13.2,
|
any.QuickCheck ==2.13.2,
|
||||||
QuickCheck +templatehaskell,
|
QuickCheck +templatehaskell,
|
||||||
any.StateVar ==1.2,
|
any.StateVar ==1.2,
|
||||||
any.abstract-deque ==0.3,
|
any.abstract-deque ==0.3,
|
||||||
abstract-deque -usecas,
|
abstract-deque -usecas,
|
||||||
|
any.abstract-par ==0.3.3,
|
||||||
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,
|
any.aeson-pretty ==0.8.8,
|
||||||
@@ -27,6 +30,7 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.auto-update ==0.1.6,
|
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-compat-batteries ==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.base16-bytestring ==0.1.1.6,
|
||||||
@@ -34,21 +38,24 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
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.binary-orphans ==1.0.1,
|
||||||
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.bytestring-handle ==0.1.0.6,
|
||||||
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.case-insensitive ==1.2.1.0,
|
any.case-insensitive ==1.2.1.0,
|
||||||
|
any.cassava ==0.5.2.0,
|
||||||
|
cassava -bytestring--lt-0_10_4,
|
||||||
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,
|
any.cmdargs ==0.10.20,
|
||||||
cmdargs +quotation -testprog,
|
cmdargs +quotation -testprog,
|
||||||
|
any.code-page ==0.2,
|
||||||
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,
|
||||||
@@ -57,10 +64,15 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
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.criterion ==1.5.6.2,
|
||||||
|
criterion -embed-data-files -fast,
|
||||||
|
any.criterion-measurement ==0.1.2.0,
|
||||||
|
criterion-measurement -fast,
|
||||||
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.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.dense-linear-algebra ==0.1.0.0,
|
||||||
any.directory ==1.3.3.0 || ==1.3.6.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,
|
||||||
@@ -99,13 +111,15 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.hsc2hs ==0.68.6,
|
any.hsc2hs ==0.68.6,
|
||||||
hsc2hs -in-ghc-tree,
|
hsc2hs -in-ghc-tree,
|
||||||
any.http-io-streams ==0.1.2.0,
|
any.http-io-streams ==0.1.2.0,
|
||||||
http-io-streams +brotli,
|
http-io-streams -brotli,
|
||||||
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,
|
||||||
integer-logarithms -check-bounds +integer-gmp,
|
integer-logarithms -check-bounds +integer-gmp,
|
||||||
any.io-streams ==1.5.1.0,
|
any.io-streams ==1.5.1.0,
|
||||||
io-streams -nointeractivetests,
|
io-streams -nointeractivetests,
|
||||||
|
any.js-flot ==0.8.3,
|
||||||
|
any.js-jquery ==3.3.1,
|
||||||
any.language-bash ==0.9.0,
|
any.language-bash ==0.9.0,
|
||||||
any.lifted-base ==0.2.3.12,
|
any.lifted-base ==0.2.3.12,
|
||||||
any.list-t ==1.0.4,
|
any.list-t ==1.0.4,
|
||||||
@@ -115,12 +129,16 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
math-functions +system-erf +system-expm1,
|
math-functions +system-erf +system-expm1,
|
||||||
any.megaparsec ==8.0.0,
|
any.megaparsec ==8.0.0,
|
||||||
megaparsec -dev,
|
megaparsec -dev,
|
||||||
|
any.microstache ==1.0.1.1,
|
||||||
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,
|
any.monad-logger ==0.3.32,
|
||||||
monad-logger +template_haskell,
|
monad-logger +template_haskell,
|
||||||
any.monad-loops ==0.4.3,
|
any.monad-loops ==0.4.3,
|
||||||
monad-loops +base4,
|
monad-loops +base4,
|
||||||
|
any.monad-par ==0.3.5,
|
||||||
|
monad-par -chaselev -newgeneric,
|
||||||
|
any.monad-par-extras ==0.3.3,
|
||||||
any.mono-traversable ==1.0.15.1,
|
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,
|
||||||
@@ -135,6 +153,7 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.optics-th ==0.2,
|
any.optics-th ==0.2,
|
||||||
any.optics-vl ==0.2,
|
any.optics-vl ==0.2,
|
||||||
any.optparse-applicative ==0.15.1.0,
|
any.optparse-applicative ==0.15.1.0,
|
||||||
|
any.parallel ==3.2.2.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,
|
||||||
@@ -150,6 +169,9 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.random ==1.1,
|
any.random ==1.1,
|
||||||
any.recursion-schemes ==5.1.3,
|
any.recursion-schemes ==5.1.3,
|
||||||
recursion-schemes +template-haskell,
|
recursion-schemes +template-haskell,
|
||||||
|
any.regex-base ==0.94.0.0,
|
||||||
|
any.regex-posix ==0.96.0.0,
|
||||||
|
regex-posix -_regex-posix-clib,
|
||||||
any.resourcet ==1.2.3,
|
any.resourcet ==1.2.3,
|
||||||
any.rts ==1.0,
|
any.rts ==1.0,
|
||||||
any.safe ==0.3.18,
|
any.safe ==0.3.18,
|
||||||
@@ -164,6 +186,7 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.split ==0.2.3.4,
|
any.split ==0.2.3.4,
|
||||||
any.splitmix ==0.0.4,
|
any.splitmix ==0.0.4,
|
||||||
splitmix -optimised-mixer +random,
|
splitmix -optimised-mixer +random,
|
||||||
|
any.statistics ==0.15.2.0,
|
||||||
any.stm ==2.5.0.0,
|
any.stm ==2.5.0.0,
|
||||||
any.stm-chans ==3.0.0.4,
|
any.stm-chans ==3.0.0.4,
|
||||||
any.streaming-commons ==0.2.1.2,
|
any.streaming-commons ==0.2.1.2,
|
||||||
@@ -178,13 +201,15 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.table-layout ==0.8.0.5,
|
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.3.0,
|
any.tar-bytestring ==0.6.3.1,
|
||||||
|
any.tasty ==1.2.3,
|
||||||
|
tasty +clock,
|
||||||
|
any.tasty-quickcheck ==0.10.1.1,
|
||||||
any.template-haskell ==2.14.0.0,
|
any.template-haskell ==2.14.0.0,
|
||||||
any.terminal-progress-bar ==0.4.1,
|
any.terminal-progress-bar ==0.4.1,
|
||||||
any.terminal-size ==0.3.2.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-conversions ==0.3.0,
|
||||||
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.th-abstraction ==0.3.2.0,
|
any.th-abstraction ==0.3.2.0,
|
||||||
@@ -204,6 +229,7 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
any.transformers-compat ==0.6.5,
|
any.transformers-compat ==0.6.5,
|
||||||
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
||||||
any.typed-process ==0.2.6.0,
|
any.typed-process ==0.2.6.0,
|
||||||
|
any.unbounded-delays ==0.1.1.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.unix-compat ==0.5.2,
|
any.unix-compat ==0.5.2,
|
||||||
@@ -220,9 +246,12 @@ constraints: any.Cabal ==2.4.0.1,
|
|||||||
vector +boundschecks -internalchecks -unsafechecks -wall,
|
vector +boundschecks -internalchecks -unsafechecks -wall,
|
||||||
any.vector-algorithms ==0.8.0.3,
|
any.vector-algorithms ==0.8.0.3,
|
||||||
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
|
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
|
||||||
|
any.vector-binary-instances ==0.2.5.1,
|
||||||
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,
|
||||||
|
any.wcwidth ==0.0.2,
|
||||||
|
wcwidth -cli +split-base,
|
||||||
any.word8 ==0.1.3,
|
any.word8 ==0.1.3,
|
||||||
any.zlib ==0.6.2.1,
|
any.zlib ==0.6.2.1,
|
||||||
zlib -non-blocking-ffi -pkg-config,
|
zlib -non-blocking-ffi -pkg-config,
|
||||||
|
|||||||
563
ghcup.cabal
563
ghcup.cabal
@@ -1,234 +1,371 @@
|
|||||||
cabal-version: 2.2
|
cabal-version: 3.0
|
||||||
|
name: ghcup
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis: ghc toolchain installer as an exe/library
|
||||||
|
description:
|
||||||
|
A rewrite of the shell script ghcup, for providing
|
||||||
|
a more stable user experience and exposing an API.
|
||||||
|
|
||||||
name: ghcup
|
homepage: https://github.com/hasufell/ghcup-hs
|
||||||
version: 0.1.0.0
|
bug-reports: https://github.com/hasufell/ghcup-hs/issues
|
||||||
synopsis: ghc toolchain installer as an exe/library
|
license: LGPL-3.0-only
|
||||||
description: A rewrite of the shell script ghcup, for providing
|
license-file: LICENSE
|
||||||
a more stable user experience and exposing an API.
|
author: Julian Ospald
|
||||||
homepage: https://github.com/hasufell/ghcup-hs
|
maintainer: hasufell@posteo.de
|
||||||
bug-reports: https://github.com/hasufell/ghcup-hs/issues
|
copyright: Julian Ospald 2020
|
||||||
license: LGPL-3.0-only
|
category: System
|
||||||
license-file: LICENSE
|
build-type: Simple
|
||||||
author: Julian Ospald
|
extra-source-files: CHANGELOG.md
|
||||||
maintainer: hasufell@posteo.de
|
|
||||||
copyright: Julian Ospald 2020
|
|
||||||
category: System
|
|
||||||
build-type: Simple
|
|
||||||
extra-source-files: CHANGELOG.md
|
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/hasufell/ghcup-hs
|
location: https://github.com/hasufell/ghcup-hs
|
||||||
|
|
||||||
common HsOpenSSL { build-depends: HsOpenSSL >= 0.11 }
|
common HsOpenSSL
|
||||||
common aeson { build-depends: aeson >= 1.4 }
|
build-depends: HsOpenSSL >=0.11
|
||||||
common aeson-pretty { build-depends: aeson-pretty >= 0.8.8 }
|
|
||||||
common ascii-string { build-depends: ascii-string >= 1.0 }
|
|
||||||
common async { build-depends: async >= 0.8 }
|
|
||||||
common attoparsec { build-depends: attoparsec >= 0.13 }
|
|
||||||
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 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 generics-sop { build-depends: generics-sop >= 0.5 }
|
|
||||||
common haskus-utils-types { build-depends: haskus-utils-types >= 1.5 }
|
|
||||||
common haskus-utils-variant { build-depends: haskus-utils-variant >= 3.0 }
|
|
||||||
common hopenssl { build-depends: hopenssl >= 2.2.4 }
|
|
||||||
common hpath { build-depends: hpath >= 0.11 }
|
|
||||||
common hpath-directory { build-depends: hpath-directory >= 0.13.2 }
|
|
||||||
common hpath-filepath { build-depends: hpath-filepath >= 0.10.3 }
|
|
||||||
common hpath-io { build-depends: hpath-io >= 0.13.1 }
|
|
||||||
common hpath-posix { build-depends: hpath-posix >= 0.11.1 }
|
|
||||||
common http-io-streams { build-depends: http-io-streams >= 0.1.2.0 }
|
|
||||||
common io-streams { build-depends: io-streams >= 1.5 }
|
|
||||||
common language-bash { build-depends: language-bash >= 0.9 }
|
|
||||||
common lzma { build-depends: lzma >= 0.0.0.3 }
|
|
||||||
common monad-logger { build-depends: monad-logger >= 0.3.31 }
|
|
||||||
common mtl { build-depends: mtl >= 2.2 }
|
|
||||||
common optics { build-depends: optics >= 0.2 }
|
|
||||||
common optics-vl { build-depends: optics-vl >= 0.2 }
|
|
||||||
common optparse-applicative { build-depends: optparse-applicative >= 0.15.1.0 }
|
|
||||||
common parsec { build-depends: parsec >= 3.1 }
|
|
||||||
common pretty-terminal { build-depends: pretty-terminal >= 0.1.0.0 }
|
|
||||||
common resourcet { build-depends: resourcet >= 1.2.2 }
|
|
||||||
common safe { build-depends: safe >= 0.3.18 }
|
|
||||||
common safe-exceptions { build-depends: safe-exceptions >= 0.1 }
|
|
||||||
common streamly { build-depends: streamly >= 0.7.1 }
|
|
||||||
common streamly-posix { build-depends: streamly-posix >= 0.1.0.0 }
|
|
||||||
common streamly-bytestring { build-depends: streamly-bytestring >= 0.1.2 }
|
|
||||||
common strict-base { build-depends: strict-base >= 0.4 }
|
|
||||||
common string-interpolate { build-depends: string-interpolate >= 0.2.0.0 }
|
|
||||||
common table-layout { build-depends: table-layout >= 0.8 }
|
|
||||||
common tar-bytestring { build-depends: tar-bytestring >= 0.6.3.0 }
|
|
||||||
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-icu { build-depends: text-icu >= 0.7 }
|
|
||||||
common time { build-depends: time >= 1.9.3 }
|
|
||||||
common transformers { build-depends: transformers >= 0.5 }
|
|
||||||
common unix { build-depends: unix >= 2.7 }
|
|
||||||
common unix-bytestring { build-depends: unix-bytestring >= 0.3 }
|
|
||||||
common uri-bytestring { build-depends: uri-bytestring >= 0.3.2.2 }
|
|
||||||
common utf8-string { build-depends: utf8-string >= 1.0 }
|
|
||||||
common vector { build-depends: vector >= 0.12 }
|
|
||||||
common versions { build-depends: versions >= 3.5 }
|
|
||||||
common waargonaut { build-depends: waargonaut >= 0.8 }
|
|
||||||
common word8 { build-depends: word8 >= 0.1.3 }
|
|
||||||
common zlib { build-depends: zlib >= 0.6.2.1 }
|
|
||||||
|
|
||||||
|
common aeson
|
||||||
|
build-depends: aeson >=1.4
|
||||||
|
|
||||||
|
common aeson-pretty
|
||||||
|
build-depends: aeson-pretty >=0.8.8
|
||||||
|
|
||||||
|
common ascii-string
|
||||||
|
build-depends: ascii-string >=1.0
|
||||||
|
|
||||||
|
common async
|
||||||
|
build-depends: async >=0.8
|
||||||
|
|
||||||
|
common attoparsec
|
||||||
|
build-depends: attoparsec >=0.13
|
||||||
|
|
||||||
|
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 bzlib
|
||||||
|
build-depends: bzlib >=0.5.0.5
|
||||||
|
|
||||||
|
common case-insensitive
|
||||||
|
build-depends: case-insensitive >=1.2.1.0
|
||||||
|
|
||||||
|
common concurrent-output
|
||||||
|
build-depends: concurrent-output >=1.10.11
|
||||||
|
|
||||||
|
common containers
|
||||||
|
build-depends: containers >=0.6
|
||||||
|
|
||||||
|
common generics-sop
|
||||||
|
build-depends: generics-sop >=0.5
|
||||||
|
|
||||||
|
common haskus-utils-types
|
||||||
|
build-depends: haskus-utils-types >=1.5
|
||||||
|
|
||||||
|
common haskus-utils-variant
|
||||||
|
build-depends: haskus-utils-variant >=3.0
|
||||||
|
|
||||||
|
common hopenssl
|
||||||
|
build-depends: hopenssl >=2.2.4
|
||||||
|
|
||||||
|
common hpath
|
||||||
|
build-depends: hpath >=0.11
|
||||||
|
|
||||||
|
common hpath-directory
|
||||||
|
build-depends: hpath-directory >=0.13.2
|
||||||
|
|
||||||
|
common hpath-filepath
|
||||||
|
build-depends: hpath-filepath >=0.10.3
|
||||||
|
|
||||||
|
common hpath-io
|
||||||
|
build-depends: hpath-io >=0.13.1
|
||||||
|
|
||||||
|
common hpath-posix
|
||||||
|
build-depends: hpath-posix >=0.11.1
|
||||||
|
|
||||||
|
common http-io-streams
|
||||||
|
build-depends: http-io-streams >=0.1.2.0
|
||||||
|
|
||||||
|
common io-streams
|
||||||
|
build-depends: io-streams >=1.5
|
||||||
|
|
||||||
|
common language-bash
|
||||||
|
build-depends: language-bash >=0.9
|
||||||
|
|
||||||
|
common lzma
|
||||||
|
build-depends: lzma >=0.0.0.3
|
||||||
|
|
||||||
|
common megaparsec
|
||||||
|
build-depends: megaparsec >=8.0.0
|
||||||
|
|
||||||
|
common monad-logger
|
||||||
|
build-depends: monad-logger >=0.3.31
|
||||||
|
|
||||||
|
common mtl
|
||||||
|
build-depends: mtl >=2.2
|
||||||
|
|
||||||
|
common optics
|
||||||
|
build-depends: optics >=0.2
|
||||||
|
|
||||||
|
common optics-vl
|
||||||
|
build-depends: optics-vl >=0.2
|
||||||
|
|
||||||
|
common optparse-applicative
|
||||||
|
build-depends: optparse-applicative >=0.15.1.0
|
||||||
|
|
||||||
|
common parsec
|
||||||
|
build-depends: parsec >=3.1
|
||||||
|
|
||||||
|
common pretty-terminal
|
||||||
|
build-depends: pretty-terminal >=0.1.0.0
|
||||||
|
|
||||||
|
common regex-posix
|
||||||
|
build-depends: regex-posix >=0.96
|
||||||
|
|
||||||
|
common resourcet
|
||||||
|
build-depends: resourcet >=1.2.2
|
||||||
|
|
||||||
|
common safe
|
||||||
|
build-depends: safe >=0.3.18
|
||||||
|
|
||||||
|
common safe-exceptions
|
||||||
|
build-depends: safe-exceptions >=0.1
|
||||||
|
|
||||||
|
common streamly
|
||||||
|
build-depends: streamly >=0.7.1
|
||||||
|
|
||||||
|
common streamly-posix
|
||||||
|
build-depends: streamly-posix >=0.1.0.0
|
||||||
|
|
||||||
|
common streamly-bytestring
|
||||||
|
build-depends: streamly-bytestring >=0.1.2
|
||||||
|
|
||||||
|
common strict-base
|
||||||
|
build-depends: strict-base >=0.4
|
||||||
|
|
||||||
|
common string-interpolate
|
||||||
|
build-depends: string-interpolate >=0.2.0.0
|
||||||
|
|
||||||
|
common table-layout
|
||||||
|
build-depends: table-layout >=0.8
|
||||||
|
|
||||||
|
common tar-bytestring
|
||||||
|
build-depends: tar-bytestring >=0.6.3.1
|
||||||
|
|
||||||
|
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 time
|
||||||
|
build-depends: time >=1.9.3
|
||||||
|
|
||||||
|
common transformers
|
||||||
|
build-depends: transformers >=0.5
|
||||||
|
|
||||||
|
common unix
|
||||||
|
build-depends: unix >=2.7
|
||||||
|
|
||||||
|
common unix-bytestring
|
||||||
|
build-depends: unix-bytestring >=0.3
|
||||||
|
|
||||||
|
common uri-bytestring
|
||||||
|
build-depends: uri-bytestring >=0.3.2.2
|
||||||
|
|
||||||
|
common utf8-string
|
||||||
|
build-depends: utf8-string >=1.0
|
||||||
|
|
||||||
|
common vector
|
||||||
|
build-depends: vector >=0.12
|
||||||
|
|
||||||
|
common versions
|
||||||
|
build-depends: versions >=3.5
|
||||||
|
|
||||||
|
common waargonaut
|
||||||
|
build-depends: waargonaut >=0.8
|
||||||
|
|
||||||
|
common word8
|
||||||
|
build-depends: word8 >=0.1.3
|
||||||
|
|
||||||
|
common zlib
|
||||||
|
build-depends: zlib >=0.6.2.1
|
||||||
|
|
||||||
common config
|
common config
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -threaded
|
ghc-options:
|
||||||
default-extensions: LambdaCase
|
-Wall -fwarn-tabs -fwarn-incomplete-uni-patterns
|
||||||
, MultiWayIf
|
-fwarn-incomplete-record-updates -threaded
|
||||||
, PackageImports
|
|
||||||
, RecordWildCards
|
default-extensions:
|
||||||
, ScopedTypeVariables
|
LambdaCase
|
||||||
, StrictData
|
MultiWayIf
|
||||||
, Strict
|
PackageImports
|
||||||
, TupleSections
|
RecordWildCards
|
||||||
|
ScopedTypeVariables
|
||||||
|
Strict
|
||||||
|
StrictData
|
||||||
|
TupleSections
|
||||||
|
|
||||||
library
|
library
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
-- deps
|
, base
|
||||||
, HsOpenSSL
|
, HsOpenSSL
|
||||||
, aeson
|
, aeson
|
||||||
, ascii-string
|
, ascii-string
|
||||||
, async
|
, async
|
||||||
, attoparsec
|
, attoparsec
|
||||||
, binary
|
, binary
|
||||||
, bytestring
|
, bytestring
|
||||||
, bzlib
|
, bzlib
|
||||||
, case-insensitive
|
, case-insensitive
|
||||||
, containers
|
, concurrent-output
|
||||||
, generics-sop
|
, containers
|
||||||
, haskus-utils-types
|
, generics-sop
|
||||||
, haskus-utils-variant
|
, haskus-utils-types
|
||||||
, hopenssl
|
, haskus-utils-variant
|
||||||
, hpath
|
, hopenssl
|
||||||
, hpath-directory
|
, hpath
|
||||||
, hpath-filepath
|
, hpath-directory
|
||||||
, hpath-io
|
, hpath-filepath
|
||||||
, hpath-posix
|
, hpath-io
|
||||||
, http-io-streams
|
, hpath-posix
|
||||||
, io-streams
|
, http-io-streams
|
||||||
, language-bash
|
, io-streams
|
||||||
, lzma
|
, language-bash
|
||||||
, monad-logger
|
, lzma
|
||||||
, mtl
|
, monad-logger
|
||||||
, optics
|
, mtl
|
||||||
, optics-vl
|
, optics
|
||||||
, parsec
|
, optics-vl
|
||||||
, pretty-terminal
|
, parsec
|
||||||
, resourcet
|
, pretty-terminal
|
||||||
, safe
|
, regex-posix
|
||||||
, safe-exceptions
|
, resourcet
|
||||||
, streamly
|
, safe
|
||||||
, streamly-posix
|
, safe-exceptions
|
||||||
, streamly-bytestring
|
, streamly
|
||||||
, strict-base
|
, streamly-posix
|
||||||
, string-interpolate
|
, streamly-bytestring
|
||||||
, tar-bytestring
|
, strict-base
|
||||||
, template-haskell
|
, string-interpolate
|
||||||
, terminal-progress-bar
|
, tar-bytestring
|
||||||
, text
|
, template-haskell
|
||||||
, text-icu
|
, terminal-progress-bar
|
||||||
, time
|
, text
|
||||||
, transformers
|
, time
|
||||||
, unix
|
, transformers
|
||||||
, unix-bytestring
|
, unix
|
||||||
, uri-bytestring
|
, unix-bytestring
|
||||||
, utf8-string
|
, uri-bytestring
|
||||||
, vector
|
, utf8-string
|
||||||
, versions
|
, vector
|
||||||
, word8
|
, versions
|
||||||
, zlib
|
, word8
|
||||||
exposed-modules: GHCup
|
, zlib
|
||||||
GHCup.Download
|
|
||||||
GHCup.Errors
|
-- deps
|
||||||
GHCup.Platform
|
-- cabal-fmt: expand lib
|
||||||
GHCup.Types
|
exposed-modules:
|
||||||
GHCup.Types.JSON
|
GHCup
|
||||||
GHCup.Types.Optics
|
GHCup.Download
|
||||||
GHCup.Utils
|
GHCup.Errors
|
||||||
GHCup.Utils.Bash
|
GHCup.Platform
|
||||||
GHCup.Utils.Dirs
|
GHCup.Types
|
||||||
GHCup.Utils.File
|
GHCup.Types.JSON
|
||||||
GHCup.Utils.Logger
|
GHCup.Types.Optics
|
||||||
GHCup.Utils.Prelude
|
GHCup.Utils
|
||||||
GHCup.Utils.String.QQ
|
GHCup.Utils.Bash
|
||||||
GHCup.Utils.Version.QQ
|
GHCup.Utils.Dirs
|
||||||
GHCup.Version
|
GHCup.Utils.File
|
||||||
|
GHCup.Utils.Logger
|
||||||
|
GHCup.Utils.Prelude
|
||||||
|
GHCup.Utils.String.QQ
|
||||||
|
GHCup.Utils.Version.QQ
|
||||||
|
GHCup.Version
|
||||||
|
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
hs-source-dirs: lib
|
hs-source-dirs: lib
|
||||||
|
|
||||||
executable ghcup
|
executable ghcup
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
--
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, haskus-utils-variant
|
, haskus-utils-variant
|
||||||
, monad-logger
|
, monad-logger
|
||||||
, mtl
|
, megaparsec
|
||||||
, optparse-applicative
|
, mtl
|
||||||
, text
|
, optparse-applicative
|
||||||
, versions
|
, text
|
||||||
, hpath
|
, versions
|
||||||
, hpath-io
|
, hpath
|
||||||
, pretty-terminal
|
, hpath-io
|
||||||
, resourcet
|
, pretty-terminal
|
||||||
, string-interpolate
|
, resourcet
|
||||||
, table-layout
|
, string-interpolate
|
||||||
, uri-bytestring
|
, table-layout
|
||||||
, utf8-string
|
, uri-bytestring
|
||||||
main-is: Main.hs
|
, utf8-string
|
||||||
|
|
||||||
|
--
|
||||||
|
main-is: Main.hs
|
||||||
|
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: ghcup
|
build-depends: ghcup
|
||||||
hs-source-dirs: app/ghcup
|
hs-source-dirs: app/ghcup
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
executable ghcup-gen
|
executable ghcup-gen
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
--
|
, base
|
||||||
, aeson
|
, aeson
|
||||||
, aeson-pretty
|
, aeson-pretty
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, safe-exceptions
|
, safe-exceptions
|
||||||
, haskus-utils-variant
|
, haskus-utils-variant
|
||||||
, monad-logger
|
, monad-logger
|
||||||
, mtl
|
, mtl
|
||||||
, optics
|
, optics
|
||||||
, optparse-applicative
|
, optparse-applicative
|
||||||
, text
|
, text
|
||||||
, versions
|
, versions
|
||||||
, hpath
|
, hpath
|
||||||
, pretty-terminal
|
, pretty-terminal
|
||||||
, resourcet
|
, resourcet
|
||||||
, string-interpolate
|
, string-interpolate
|
||||||
, table-layout
|
, table-layout
|
||||||
, transformers
|
, transformers
|
||||||
, uri-bytestring
|
, uri-bytestring
|
||||||
, utf8-string
|
, utf8-string
|
||||||
main-is: Main.hs
|
|
||||||
other-modules: GHCupDownloads
|
--
|
||||||
Validate
|
main-is: Main.hs
|
||||||
|
other-modules:
|
||||||
|
GHCupDownloads
|
||||||
|
Validate
|
||||||
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: ghcup
|
build-depends: ghcup
|
||||||
hs-source-dirs: app/ghcup-gen
|
hs-source-dirs: app/ghcup-gen
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
test-suite ghcup-test
|
test-suite ghcup-test
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: MyLibTest.hs
|
main-is: MyLibTest.hs
|
||||||
build-depends: base ^>=4.12.0.0
|
build-depends: base >=4.12.0.0
|
||||||
|
|||||||
152
lib/GHCup.hs
152
lib/GHCup.hs
@@ -3,10 +3,11 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
module GHCup where
|
module GHCup where
|
||||||
|
|
||||||
@@ -108,14 +109,18 @@ installGHCBin bDls ver mpfReq = do
|
|||||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||||
|
|
||||||
-- 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 workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
catchAllE
|
-- Be careful about cleanup. We must catch both pure exceptions
|
||||||
(\es ->
|
-- as well as async ones.
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
flip onException
|
||||||
>> throwE (BuildFailed archiveSubdir es)
|
(liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
)
|
$ catchAllE
|
||||||
$ installGHC' archiveSubdir ghcdir
|
(\es -> do
|
||||||
|
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
|
>> throwE (BuildFailed workdir es)
|
||||||
|
)
|
||||||
|
$ installGHC' workdir ghcdir
|
||||||
|
|
||||||
-- only clean up dir if the build succeeded
|
-- only clean up dir if the build succeeded
|
||||||
liftIO $ deleteDirRecursive tmpUnpack
|
liftIO $ deleteDirRecursive tmpUnpack
|
||||||
@@ -129,19 +134,14 @@ installGHCBin bDls ver mpfReq = do
|
|||||||
-> Path Abs -- ^ Path to install to
|
-> Path Abs -- ^ Path to install to
|
||||||
-> Excepts '[ProcessError] m ()
|
-> Excepts '[ProcessError] m ()
|
||||||
installGHC' path inst = do
|
installGHC' path inst = do
|
||||||
lift $ $(logInfo) [s|Installing GHC (this may take a while)|]
|
lift $ $(logInfo) "Installing GHC (this may take a while)"
|
||||||
lEM $ liftIO $ execLogged [s|./configure|]
|
lEM $ liftIO $ execLogged "./configure"
|
||||||
False
|
False
|
||||||
[[s|--prefix=|] <> toFilePath inst]
|
["--prefix=" <> toFilePath inst]
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
[rel|ghc-configure|]
|
||||||
(Just path)
|
|
||||||
Nothing
|
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
|
||||||
True
|
|
||||||
[[s|install|]]
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just path)
|
(Just path)
|
||||||
Nothing
|
Nothing
|
||||||
|
lEM $ liftIO $ make ["install"] (Just path)
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
|
||||||
@@ -183,9 +183,9 @@ installCabalBin bDls ver mpfReq = do
|
|||||||
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 workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
liftE $ installCabal' archiveSubdir bindir
|
liftE $ installCabal' workdir bindir
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
@@ -195,8 +195,8 @@ installCabalBin bDls ver mpfReq = do
|
|||||||
-> Path Abs -- ^ Path to install to
|
-> Path Abs -- ^ Path to install to
|
||||||
-> Excepts '[CopyError] m ()
|
-> Excepts '[CopyError] m ()
|
||||||
installCabal' path inst = do
|
installCabal' path inst = do
|
||||||
lift $ $(logInfo) [s|Installing cabal|]
|
lift $ $(logInfo) "Installing cabal"
|
||||||
let cabalFile = [rel|cabal|] :: Path Rel
|
let cabalFile = [rel|cabal|]
|
||||||
liftIO $ createDirIfMissing newDirPerms inst
|
liftIO $ createDirIfMissing newDirPerms inst
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> cabalFile)
|
(path </> cabalFile)
|
||||||
@@ -247,7 +247,7 @@ setGHC ver sghc = do
|
|||||||
SetGHCOnly -> pure file
|
SetGHCOnly -> pure file
|
||||||
SetGHC_XY -> do
|
SetGHC_XY -> do
|
||||||
major' <-
|
major' <-
|
||||||
(\(mj, mi) -> E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi)
|
(\(mj, mi) -> E.encodeUtf8 $ intToText mj <> "." <> intToText mi)
|
||||||
<$> getGHCMajor ver
|
<$> getGHCMajor ver
|
||||||
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
||||||
SetGHC_XYZ -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
SetGHC_XYZ -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
||||||
@@ -273,11 +273,11 @@ setGHC ver sghc = do
|
|||||||
destdir <- liftIO $ ghcupBaseDir
|
destdir <- liftIO $ ghcupBaseDir
|
||||||
case sghc of
|
case sghc of
|
||||||
SetGHCOnly -> do
|
SetGHCOnly -> do
|
||||||
let sharedir = [rel|share|] :: Path Rel
|
let sharedir = [rel|share|]
|
||||||
let fullsharedir = ghcdir </> sharedir
|
let fullsharedir = ghcdir </> sharedir
|
||||||
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
||||||
let fullF = destdir </> sharedir
|
let fullF = destdir </> sharedir
|
||||||
let targetF = [s|./ghc/|] <> verBS <> [s|/|] <> toFilePath sharedir
|
let targetF = "./ghc/" <> verBS <> "/" <> toFilePath sharedir
|
||||||
$(logDebug) [i|rm -f #{fullF}|]
|
$(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
||||||
@@ -335,12 +335,12 @@ listVersions av lt criteria = case lt of
|
|||||||
fromSrc <- ghcSrcInstalled 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
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
||||||
GHCup -> do
|
GHCup -> do
|
||||||
let lSet = prettyPVP ghcUpVer == prettyVer v
|
let lSet = prettyPVP ghcUpVer == prettyVer v
|
||||||
let lInstalled = True
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
||||||
|
|
||||||
|
|
||||||
@@ -393,7 +393,7 @@ rmGHCVer ver = do
|
|||||||
$ ghcupBaseDir
|
$ ghcupBaseDir
|
||||||
>>= hideError doesNotExistErrorType
|
>>= hideError doesNotExistErrorType
|
||||||
. deleteFile
|
. deleteFile
|
||||||
. (</> ([rel|share|] :: Path Rel))
|
. (</> [rel|share|])
|
||||||
else throwE (NotInstalled GHC ver)
|
else throwE (NotInstalled GHC ver)
|
||||||
|
|
||||||
|
|
||||||
@@ -404,19 +404,18 @@ rmGHCVer ver = do
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadReader Settings m, MonadIO m)
|
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Excepts
|
=> Excepts
|
||||||
'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
||||||
m
|
m
|
||||||
DebugInfo
|
DebugInfo
|
||||||
getDebugInfo = do
|
getDebugInfo = do
|
||||||
diBaseDir <- liftIO $ ghcupBaseDir
|
diBaseDir <- liftIO $ ghcupBaseDir
|
||||||
diBinDir <- liftIO $ ghcupBinDir
|
diBinDir <- liftIO $ ghcupBinDir
|
||||||
diGHCDir <- liftIO $ ghcupGHCBaseDir
|
diGHCDir <- liftIO $ ghcupGHCBaseDir
|
||||||
diCacheDir <- liftIO $ ghcupCacheDir
|
diCacheDir <- liftIO $ ghcupCacheDir
|
||||||
diURLSource <- lift $ getUrlSource
|
diArch <- lE getArchitecture
|
||||||
diArch <- lE getArchitecture
|
diPlatform <- liftE $ getPlatform
|
||||||
diPlatform <- liftE $ getPlatform
|
|
||||||
pure $ DebugInfo { .. }
|
pure $ DebugInfo { .. }
|
||||||
|
|
||||||
|
|
||||||
@@ -464,15 +463,19 @@ compileGHC dls tver bver jobs mbuildConfig = do
|
|||||||
tmpUnpack <- lift mkGhcupTmpDir
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
|
||||||
bghc <- parseRel ([s|ghc-|] <> verToBS bver)
|
bghc <- parseRel ("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
|
||||||
|
|
||||||
catchAllE
|
-- Be careful about cleanup. We must catch both pure exceptions
|
||||||
(\es ->
|
-- as well as async ones.
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
flip onException
|
||||||
>> throwE (BuildFailed workdir es)
|
(liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
)
|
$ catchAllE
|
||||||
|
(\es ->
|
||||||
|
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
||||||
|
>> throwE (BuildFailed workdir es)
|
||||||
|
)
|
||||||
$ compile bghc ghcdir workdir
|
$ compile bghc ghcdir workdir
|
||||||
markSrcBuilt ghcdir workdir
|
markSrcBuilt ghcdir workdir
|
||||||
|
|
||||||
@@ -501,27 +504,29 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
()
|
()
|
||||||
compile bghc ghcdir workdir = do
|
compile bghc ghcdir workdir = do
|
||||||
lift $ $(logInfo) [i|configuring build|]
|
lift $ $(logInfo) [i|configuring build|]
|
||||||
|
|
||||||
|
-- force ld.bfd for build (others seem to misbehave, like lld from FreeBSD)
|
||||||
|
newEnv <- addToCurrentEnv [("LD", "ld.bfd")]
|
||||||
|
|
||||||
if
|
if
|
||||||
| tver >= [vver|8.8.0|] -> do
|
| tver >= [vver|8.8.0|] -> do
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
||||||
bghcPath <- (liftIO $ searchPath spaths bghc) !? NoDownload
|
bghcPath <- (liftIO $ searchPath spaths bghc) !? NoDownload
|
||||||
newEnv <- addToCurrentEnv [([s|GHC|], toFilePath bghcPath)]
|
lEM $ liftIO $ execLogged
|
||||||
lEM $ liftIO $ execLogged [s|./configure|]
|
"./configure"
|
||||||
False
|
False
|
||||||
[[s|--prefix=|] <> toFilePath ghcdir]
|
["--prefix=" <> toFilePath ghcdir]
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
[rel|ghc-conf|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just newEnv)
|
(Just (("GHC", toFilePath bghcPath) : newEnv))
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
lEM $ liftIO $ execLogged
|
lEM $ liftIO $ execLogged
|
||||||
[s|./configure|]
|
"./configure"
|
||||||
False
|
False
|
||||||
[ [s|--prefix=|] <> toFilePath ghcdir
|
["--prefix=" <> toFilePath ghcdir, "--with-ghc=" <> toFilePath bghc]
|
||||||
, [s|--with-ghc=|] <> toFilePath bghc
|
[rel|ghc-conf|]
|
||||||
]
|
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
Nothing
|
(Just newEnv)
|
||||||
|
|
||||||
case mbuildConfig of
|
case mbuildConfig of
|
||||||
Just bc -> liftIOException
|
Just bc -> liftIOException
|
||||||
@@ -534,26 +539,17 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
lift
|
lift
|
||||||
$ $(logInfo)
|
$ $(logInfo)
|
||||||
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/ghc-make.log' to see the progress.|]
|
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/ghc-make.log' to see the progress.|]
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
lEM $ liftIO $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs)
|
||||||
True
|
(Just workdir)
|
||||||
(maybe [] (\j -> [[s|-j|] <> fS (show j)]) jobs)
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Installing...|]
|
lift $ $(logInfo) [i|Installing...|]
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
lEM $ liftIO $ make ["install"] (Just workdir)
|
||||||
True
|
|
||||||
[[s|install|]]
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
markSrcBuilt ghcdir workdir = do
|
markSrcBuilt ghcdir workdir = do
|
||||||
let dest = (ghcdir </> ghcUpSrcBuiltFile)
|
let dest = (ghcdir </> ghcUpSrcBuiltFile)
|
||||||
liftIO $ copyFile (build_mk workdir) dest Overwrite
|
liftIO $ copyFile (build_mk workdir) dest Overwrite
|
||||||
|
|
||||||
build_mk workdir = workdir </> ([rel|mk/build.mk|] :: Path Rel)
|
build_mk workdir = workdir </> [rel|mk/build.mk|]
|
||||||
|
|
||||||
|
|
||||||
compileCabal :: ( MonadReader Settings m
|
compileCabal :: ( MonadReader Settings m
|
||||||
@@ -607,16 +603,16 @@ compileCabal dls tver bver jobs = do
|
|||||||
let v' = verToBS bver
|
let v' = verToBS bver
|
||||||
cabal_bin <- liftIO $ ghcupBinDir
|
cabal_bin <- liftIO $ ghcupBinDir
|
||||||
newEnv <- lift $ addToCurrentEnv
|
newEnv <- lift $ addToCurrentEnv
|
||||||
[ ([s|GHC|] , [s|ghc-|] <> v')
|
[ ("GHC" , "ghc-" <> v')
|
||||||
, ([s|GHC_PKG|], [s|ghc-pkg-|] <> v')
|
, ("GHC_PKG", "ghc-pkg-" <> v')
|
||||||
, ([s|GHC_VER|], v')
|
, ("GHC_VER", v')
|
||||||
, ([s|PREFIX|] , toFilePath cabal_bin)
|
, ("PREFIX" , toFilePath cabal_bin)
|
||||||
]
|
]
|
||||||
|
|
||||||
lEM $ liftIO $ execLogged [s|./bootstrap.sh|]
|
lEM $ liftIO $ execLogged "./bootstrap.sh"
|
||||||
False
|
False
|
||||||
(maybe [] (\j -> [[s|-j|], fS (show j)]) jobs)
|
(maybe [] (\j -> ["-j", fS (show j)]) jobs)
|
||||||
([rel|cabal-bootstrap.log|] :: Path Rel)
|
[rel|cabal-bootstrap|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just newEnv)
|
(Just newEnv)
|
||||||
|
|
||||||
@@ -651,10 +647,10 @@ upgradeGHCup :: ( MonadMask m
|
|||||||
Version
|
Version
|
||||||
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 = fromJust $ getLatest dls GHCup
|
||||||
dli <- liftE $ getDownloadInfo dls 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|]
|
||||||
p <- liftE $ download dli tmp (Just fn)
|
p <- liftE $ download dli tmp (Just fn)
|
||||||
case mtarget of
|
case mtarget of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCup.Download where
|
module GHCup.Download where
|
||||||
@@ -18,7 +19,6 @@ import GHCup.Types.Optics
|
|||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -43,7 +43,7 @@ import Data.Time.Format
|
|||||||
import Data.Versions
|
import Data.Versions
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO
|
import HPath.IO as HIO
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Network.Http.Client hiding ( URL )
|
import Network.Http.Client hiding ( URL )
|
||||||
import OpenSSL.Digest
|
import OpenSSL.Digest
|
||||||
@@ -93,21 +93,20 @@ getDownloads :: ( FromJSONKey Tool
|
|||||||
, FromJSON VersionInfo
|
, FromJSON VersionInfo
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadCatch m
|
, MonadCatch m
|
||||||
, MonadReader Settings m
|
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> Excepts '[JSONError , DownloadFailed] m GHCupDownloads
|
=> URLSource
|
||||||
getDownloads = do
|
-> Excepts '[JSONError , DownloadFailed] m GHCupDownloads
|
||||||
urlSource <- lift getUrlSource
|
getDownloads urlSource = do
|
||||||
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 <- reThrowAll DownloadFailed $ dl ghcupURL
|
bs <- reThrowAll DownloadFailed $ smartDl ghcupURL
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSource url) -> do
|
(OwnSource url) -> do
|
||||||
bs <- reThrowAll DownloadFailed $ dl url
|
bs <- reThrowAll DownloadFailed $ downloadBS url
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSpec av) -> pure $ av
|
(OwnSpec av) -> pure $ av
|
||||||
|
|
||||||
@@ -121,24 +120,24 @@ getDownloads = do
|
|||||||
-- than the local file.
|
-- than the local file.
|
||||||
--
|
--
|
||||||
-- Always save the local file with the mod time of the remote file.
|
-- Always save the local file with the mod time of the remote file.
|
||||||
dl :: forall m1
|
smartDl :: forall m1
|
||||||
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
||||||
=> URI
|
=> URI
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ FileDoesNotExistError
|
'[ FileDoesNotExistError
|
||||||
, HTTPStatusError
|
, HTTPStatusError
|
||||||
, URIParseError
|
, URIParseError
|
||||||
, UnsupportedScheme
|
, UnsupportedScheme
|
||||||
, NoLocationHeader
|
, NoLocationHeader
|
||||||
, TooManyRedirs
|
, TooManyRedirs
|
||||||
]
|
]
|
||||||
m1
|
m1
|
||||||
L.ByteString
|
L.ByteString
|
||||||
dl uri' = do
|
smartDl uri' = do
|
||||||
let path = view pathL' uri'
|
let path = view pathL' uri'
|
||||||
json_file <- (liftIO $ ghcupCacheDir)
|
cacheDir <- liftIO $ ghcupCacheDir
|
||||||
>>= \cacheDir -> (cacheDir </>) <$> urlBaseName path
|
json_file <- (cacheDir </>) <$> urlBaseName path
|
||||||
e <- liftIO $ doesFileExist json_file
|
e <- liftIO $ doesFileExist json_file
|
||||||
if e
|
if e
|
||||||
then do
|
then do
|
||||||
accessTime <-
|
accessTime <-
|
||||||
@@ -165,6 +164,7 @@ getDownloads = do
|
|||||||
else -- access in less than 5 minutes, re-use file
|
else -- access in less than 5 minutes, re-use file
|
||||||
liftIO $ readFile json_file
|
liftIO $ readFile json_file
|
||||||
else do
|
else do
|
||||||
|
liftIO $ createDirIfMissing newDirPerms cacheDir
|
||||||
getModTime >>= \case
|
getModTime >>= \case
|
||||||
Just modTime -> do
|
Just modTime -> do
|
||||||
bs <- liftE $ downloadBS uri'
|
bs <- liftE $ downloadBS uri'
|
||||||
@@ -190,7 +190,7 @@ getDownloads = do
|
|||||||
|
|
||||||
parseModifiedHeader :: (M.Map (CI ByteString) ByteString) -> Maybe UTCTime
|
parseModifiedHeader :: (M.Map (CI ByteString) ByteString) -> Maybe UTCTime
|
||||||
parseModifiedHeader headers =
|
parseModifiedHeader headers =
|
||||||
(M.lookup (CI.mk [s|Last-Modified|]) headers) >>= \h -> parseTimeM
|
(M.lookup (CI.mk "Last-Modified") headers) >>= \h -> parseTimeM
|
||||||
True
|
True
|
||||||
defaultTimeLocale
|
defaultTimeLocale
|
||||||
"%a, %d %b %Y %H:%M:%S %Z"
|
"%a, %d %b %Y %H:%M:%S %Z"
|
||||||
@@ -204,11 +204,7 @@ getDownloads = do
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
getDownloadInfo :: ( MonadLogger m
|
getDownloadInfo :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
, MonadCatch m
|
|
||||||
, MonadIO m
|
|
||||||
, MonadReader Settings m
|
|
||||||
)
|
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Tool
|
-> Tool
|
||||||
-> Version
|
-> Version
|
||||||
@@ -275,9 +271,9 @@ download :: ( MonadMask m
|
|||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe (Path Rel) -- ^ optional filename
|
||||||
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
||||||
download dli dest mfn
|
download dli dest mfn
|
||||||
| scheme == [s|https|] = dl
|
| scheme == "https" = dl
|
||||||
| scheme == [s|http|] = dl
|
| scheme == "http" = dl
|
||||||
| scheme == [s|file|] = cp
|
| scheme == "file" = cp
|
||||||
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
||||||
|
|
||||||
where
|
where
|
||||||
@@ -303,8 +299,14 @@ download dli dest mfn
|
|||||||
-- download
|
-- download
|
||||||
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
||||||
let stepper = fdWrite fd
|
let stepper = fdWrite fd
|
||||||
flip finally (liftIO $ closeFd fd)
|
flip onException
|
||||||
$ reThrowAll DownloadFailed
|
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
||||||
|
$ flip finally (liftIO $ closeFd fd)
|
||||||
|
$ catchAllE
|
||||||
|
(\e ->
|
||||||
|
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
||||||
|
>> (throwE . DownloadFailed $ e)
|
||||||
|
)
|
||||||
$ downloadInternal True https host fullPath port stepper
|
$ downloadInternal True https host fullPath port stepper
|
||||||
|
|
||||||
liftE $ checkDigest dli destFile
|
liftE $ checkDigest dli destFile
|
||||||
@@ -368,11 +370,11 @@ downloadBS :: (MonadCatch m, MonadIO m)
|
|||||||
m
|
m
|
||||||
L.ByteString
|
L.ByteString
|
||||||
downloadBS uri'
|
downloadBS uri'
|
||||||
| scheme == [s|https|]
|
| scheme == "https"
|
||||||
= dl True
|
= dl True
|
||||||
| scheme == [s|http|]
|
| scheme == "http"
|
||||||
= dl False
|
= dl False
|
||||||
| scheme == [s|file|]
|
| scheme == "file"
|
||||||
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
||||||
$ (liftIO $ RD.readFile path)
|
$ (liftIO $ RD.readFile path)
|
||||||
| otherwise
|
| otherwise
|
||||||
@@ -445,7 +447,7 @@ downloadInternal = go (5 :: Int)
|
|||||||
let scode = getStatusCode r
|
let scode = getStatusCode r
|
||||||
if
|
if
|
||||||
| scode >= 200 && scode < 300 -> downloadStream r i' >> pure Nothing
|
| scode >= 200 && scode < 300 -> downloadStream r i' >> pure Nothing
|
||||||
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
| scode >= 300 && scode < 400 -> case getHeader r "Location" of
|
||||||
Just r' -> pure $ Just $ r'
|
Just r' -> pure $ Just $ r'
|
||||||
Nothing -> throwE NoLocationHeader
|
Nothing -> throwE NoLocationHeader
|
||||||
| otherwise -> throwE $ HTTPStatusError scode
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
@@ -458,7 +460,7 @@ downloadInternal = go (5 :: Int)
|
|||||||
Left e -> throwE e
|
Left e -> throwE e
|
||||||
|
|
||||||
downloadStream r i' = do
|
downloadStream r i' = do
|
||||||
let size = case getHeader r [s|Content-Length|] of
|
let size = case getHeader r "Content-Length" of
|
||||||
Just x' -> case decimal $ E.decodeUtf8 x' of
|
Just x' -> case decimal $ E.decodeUtf8 x' of
|
||||||
Left _ -> 0
|
Left _ -> 0
|
||||||
Right (r', _) -> r'
|
Right (r', _) -> r'
|
||||||
@@ -490,9 +492,9 @@ getHead :: (MonadCatch m, MonadIO m)
|
|||||||
]
|
]
|
||||||
m
|
m
|
||||||
(M.Map (CI ByteString) ByteString)
|
(M.Map (CI ByteString) ByteString)
|
||||||
getHead uri' | scheme == [s|https|] = head' True
|
getHead uri' | scheme == "https" = head' True
|
||||||
| scheme == [s|http|] = head' False
|
| scheme == "http" = head' False
|
||||||
| otherwise = throwE UnsupportedScheme
|
| otherwise = throwE UnsupportedScheme
|
||||||
|
|
||||||
where
|
where
|
||||||
scheme = view (uriSchemeL' % schemeBSL') uri'
|
scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||||
@@ -540,7 +542,7 @@ headInternal = go (5 :: Int)
|
|||||||
| scode >= 200 && scode < 300 -> do
|
| scode >= 200 && scode < 300 -> do
|
||||||
let headers = getHeaderMap r
|
let headers = getHeaderMap r
|
||||||
pure $ Right $ headers
|
pure $ Right $ headers
|
||||||
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
| scode >= 300 && scode < 400 -> case getHeader r "Location" of
|
||||||
Just r' -> pure $ Left $ r'
|
Just r' -> pure $ Left $ r'
|
||||||
Nothing -> throwE NoLocationHeader
|
Nothing -> throwE NoLocationHeader
|
||||||
| otherwise -> throwE $ HTTPStatusError scode
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
@@ -583,19 +585,17 @@ uriToQuadruple URI {..} = do
|
|||||||
?? UnsupportedScheme
|
?? UnsupportedScheme
|
||||||
|
|
||||||
https <- if
|
https <- if
|
||||||
| scheme == [s|https|] -> pure True
|
| scheme == "https" -> pure True
|
||||||
| scheme == [s|http|] -> pure False
|
| scheme == "http" -> pure False
|
||||||
| otherwise -> throwE UnsupportedScheme
|
| otherwise -> throwE UnsupportedScheme
|
||||||
|
|
||||||
let
|
let queryBS =
|
||||||
queryBS =
|
BS.intercalate "&"
|
||||||
BS.intercalate [s|&|]
|
. fmap (\(x, y) -> encodeQuery x <> "=" <> encodeQuery y)
|
||||||
. fmap (\(x, y) -> encodeQuery x <> [s|=|] <> encodeQuery y)
|
$ (queryPairs uriQuery)
|
||||||
$ (queryPairs uriQuery)
|
port =
|
||||||
port =
|
preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority
|
||||||
preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority
|
fullpath = if BS.null queryBS then uriPath else uriPath <> "?" <> queryBS
|
||||||
fullpath =
|
|
||||||
if BS.null queryBS then uriPath else uriPath <> [s|?|] <> queryBS
|
|
||||||
pure (https, host, fullpath, port)
|
pure (https, host, fullpath, port)
|
||||||
where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
|
where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ checkDigest dli file = do
|
|||||||
verify <- lift ask <&> (not . noVerify)
|
verify <- lift ask <&> (not . noVerify)
|
||||||
when verify $ do
|
when verify $ do
|
||||||
let p' = toFilePath file
|
let p' = toFilePath file
|
||||||
lift $ $(logInfo) [i|veryfing digest of: #{p'}|]
|
lift $ $(logInfo) [i|verifying digest of: #{p'}|]
|
||||||
c <- liftIO $ readFile file
|
c <- liftIO $ readFile file
|
||||||
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
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ import Control.Monad
|
|||||||
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 )
|
||||||
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
@@ -34,10 +37,10 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.Info
|
import System.Info
|
||||||
|
import Text.Regex.Posix
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified Data.Text.ICU as ICU
|
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
--[ Platform detection ]--
|
--[ Platform detection ]--
|
||||||
@@ -100,16 +103,11 @@ getLinuxDistro = do
|
|||||||
| otherwise -> UnknownLinux
|
| otherwise -> UnknownLinux
|
||||||
pure (distro, parsedVer)
|
pure (distro, parsedVer)
|
||||||
where
|
where
|
||||||
hasWord t matches = foldr
|
hasWord t matches = foldr (\x y -> match (regex x) (T.unpack t) || y)
|
||||||
(\x y ->
|
False
|
||||||
( isJust
|
matches
|
||||||
. ICU.find (ICU.regex [ICU.CaseInsensitive] ([s|\b|] <> x <> [s|\b|]))
|
where
|
||||||
$ t
|
regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])
|
||||||
)
|
|
||||||
|| y
|
|
||||||
)
|
|
||||||
False
|
|
||||||
(T.pack <$> matches)
|
|
||||||
|
|
||||||
os_release :: Path Abs
|
os_release :: Path Abs
|
||||||
os_release = [abs|/etc/os-release|]
|
os_release = [abs|/etc/os-release|]
|
||||||
@@ -131,8 +129,8 @@ getLinuxDistro = do
|
|||||||
try_lsb_release_cmd :: IO (Text, Maybe Text)
|
try_lsb_release_cmd :: IO (Text, Maybe Text)
|
||||||
try_lsb_release_cmd = do
|
try_lsb_release_cmd = do
|
||||||
(Just _) <- findExecutable lsb_release_cmd
|
(Just _) <- findExecutable lsb_release_cmd
|
||||||
name <- fmap _stdOut $ executeOut lsb_release_cmd [[s|-si|]] Nothing
|
name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing
|
||||||
ver <- fmap _stdOut $ executeOut lsb_release_cmd [[s|-sr|]] Nothing
|
ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing
|
||||||
pure (E.decodeUtf8 name, Just $ E.decodeUtf8 ver)
|
pure (E.decodeUtf8 name, Just $ E.decodeUtf8 ver)
|
||||||
|
|
||||||
try_lsb_release :: IO (Text, Maybe Text)
|
try_lsb_release :: IO (Text, Maybe Text)
|
||||||
@@ -144,21 +142,24 @@ getLinuxDistro = do
|
|||||||
try_redhat_release :: IO (Text, Maybe Text)
|
try_redhat_release :: IO (Text, Maybe Text)
|
||||||
try_redhat_release = do
|
try_redhat_release = do
|
||||||
t <- fmap lBS2sT $ readFile redhat_release
|
t <- fmap lBS2sT $ readFile redhat_release
|
||||||
|
let nameRegex n =
|
||||||
|
makeRegexOpts compIgnoreCase
|
||||||
|
execBlank
|
||||||
|
(([s|\<|] <> fS n <> [s|\>|] :: ByteString)) :: Regex
|
||||||
|
let verRegex =
|
||||||
|
makeRegexOpts compIgnoreCase
|
||||||
|
execBlank
|
||||||
|
([s|\<([0-9])+(.([0-9])+)*\>|] :: ByteString) :: Regex
|
||||||
let nameRe n =
|
let nameRe n =
|
||||||
join
|
fromEmpty . match (nameRegex n) $ T.unpack t :: Maybe String
|
||||||
. fmap (ICU.group 0)
|
verRe = fromEmpty . match verRegex $ T.unpack t :: Maybe String
|
||||||
. ICU.find
|
|
||||||
(ICU.regex [ICU.CaseInsensitive] ([s|\b|] <> fS n <> [s|\b|]))
|
|
||||||
$ t
|
|
||||||
verRe =
|
|
||||||
join
|
|
||||||
. fmap (ICU.group 0)
|
|
||||||
. ICU.find
|
|
||||||
(ICU.regex [ICU.CaseInsensitive] [s|\b(\d)+(.(\d)+)*\b|])
|
|
||||||
$ t
|
|
||||||
(Just name) <- pure
|
(Just name) <- pure
|
||||||
(nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")
|
(nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")
|
||||||
pure (name, verRe)
|
pure (T.pack name, fmap T.pack verRe)
|
||||||
|
where
|
||||||
|
fromEmpty :: String -> Maybe String
|
||||||
|
fromEmpty "" = Nothing
|
||||||
|
fromEmpty s' = Just s'
|
||||||
|
|
||||||
try_debian_version :: IO (Text, Maybe Text)
|
try_debian_version :: IO (Text, Maybe Text)
|
||||||
try_debian_version = do
|
try_debian_version = do
|
||||||
|
|||||||
@@ -104,21 +104,19 @@ data URLSource = GHCupURL
|
|||||||
|
|
||||||
|
|
||||||
data Settings = Settings
|
data Settings = Settings
|
||||||
{ cache :: Bool
|
{ cache :: Bool
|
||||||
, urlSource :: URLSource
|
, noVerify :: Bool
|
||||||
, noVerify :: Bool
|
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
data DebugInfo = DebugInfo
|
data DebugInfo = DebugInfo
|
||||||
{ diBaseDir :: Path Abs
|
{ diBaseDir :: Path Abs
|
||||||
, diBinDir :: Path Abs
|
, diBinDir :: Path Abs
|
||||||
, diGHCDir :: Path Abs
|
, diGHCDir :: Path Abs
|
||||||
, diCacheDir :: Path Abs
|
, diCacheDir :: Path Abs
|
||||||
, diURLSource :: URLSource
|
, diArch :: Architecture
|
||||||
, diArch :: Architecture
|
, diPlatform :: PlatformResult
|
||||||
, diPlatform :: PlatformResult
|
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
@@ -141,4 +139,3 @@ data PlatformRequest = PlatformRequest
|
|||||||
, _rVersion :: Maybe Versioning
|
, _rVersion :: Maybe Versioning
|
||||||
}
|
}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
@@ -13,7 +14,6 @@ module GHCup.Types.JSON where
|
|||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Aeson.TH
|
import Data.Aeson.TH
|
||||||
@@ -138,7 +138,7 @@ instance FromJSONKey Tool where
|
|||||||
instance ToJSON (Path Rel) where
|
instance ToJSON (Path Rel) where
|
||||||
toJSON p = case and . fmap isAscii . BS.unpack $ fp of
|
toJSON p = case and . fmap isAscii . BS.unpack $ fp of
|
||||||
True -> toJSON . E.decodeUtf8 $ fp
|
True -> toJSON . E.decodeUtf8 $ fp
|
||||||
False -> String [s|/not/a/valid/path|]
|
False -> String "/not/a/valid/path"
|
||||||
where fp = toFilePath p
|
where fp = toFilePath p
|
||||||
|
|
||||||
instance FromJSON (Path Rel) where
|
instance FromJSON (Path Rel) where
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
@@ -17,7 +18,6 @@ import GHCup.Types.JSON ( )
|
|||||||
import GHCup.Utils.Dirs
|
import GHCup.Utils.Dirs
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -44,7 +44,9 @@ import Prelude hiding ( abs
|
|||||||
)
|
)
|
||||||
import Safe
|
import Safe
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.FilePath ( takeFileName )
|
import System.Posix.FilePath ( getSearchPath
|
||||||
|
, takeFileName
|
||||||
|
)
|
||||||
import System.Posix.Files.ByteString ( readSymbolicLink )
|
import System.Posix.Files.ByteString ( readSymbolicLink )
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
@@ -70,14 +72,14 @@ import qualified Data.Text.Encoding as E
|
|||||||
ghcLinkDestination :: ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
ghcLinkDestination :: ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
||||||
-> Version
|
-> Version
|
||||||
-> ByteString
|
-> ByteString
|
||||||
ghcLinkDestination tool ver = [s|../ghc/|] <> verToBS ver <> [s|/bin/|] <> tool
|
ghcLinkDestination tool ver = "../ghc/" <> verToBS ver <> "/bin/" <> tool
|
||||||
|
|
||||||
|
|
||||||
-- | Extract the version part of the result of `ghcLinkDestination`.
|
-- | Extract the version part of the result of `ghcLinkDestination`.
|
||||||
ghcLinkVersion :: MonadThrow m => ByteString -> m Version
|
ghcLinkVersion :: MonadThrow m => ByteString -> m Version
|
||||||
ghcLinkVersion = either (throwM . ParseError) pure . parseOnly parser
|
ghcLinkVersion = either (throwM . ParseError) pure . parseOnly parser
|
||||||
where
|
where
|
||||||
parser = string [s|../ghc/|] *> verParser <* string [s|/bin/ghc|]
|
parser = string "../ghc/" *> verParser <* string "/bin/ghc"
|
||||||
verParser = many1' (notWord8 _slash) >>= \t ->
|
verParser = many1' (notWord8 _slash) >>= \t ->
|
||||||
case version $ E.decodeUtf8 $ B.pack t of
|
case version $ E.decodeUtf8 $ B.pack t of
|
||||||
Left e -> fail $ show e
|
Left e -> fail $ show e
|
||||||
@@ -90,7 +92,7 @@ rmMinorSymlinks ver = do
|
|||||||
bindir <- liftIO $ ghcupBinDir
|
bindir <- liftIO $ ghcupBinDir
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
let myfiles =
|
let myfiles =
|
||||||
filter (\x -> ([s|-|] <> verToBS ver) `B.isSuffixOf` toFilePath x) files
|
filter (\x -> ("-" <> verToBS ver) `B.isSuffixOf` toFilePath x) files
|
||||||
forM_ myfiles $ \f -> do
|
forM_ myfiles $ \f -> do
|
||||||
let fullF = (bindir </> f)
|
let fullF = (bindir </> f)
|
||||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
@@ -117,12 +119,12 @@ rmPlain ver = do
|
|||||||
rmMajorSymlinks :: (MonadLogger m, MonadIO m) => Version -> m ()
|
rmMajorSymlinks :: (MonadLogger m, MonadIO m) => Version -> m ()
|
||||||
rmMajorSymlinks ver = do
|
rmMajorSymlinks ver = do
|
||||||
(mj, mi) <- liftIO $ getGHCMajor ver
|
(mj, mi) <- liftIO $ getGHCMajor ver
|
||||||
let v' = E.encodeUtf8 $ intToText mj <> [s|.|] <> intToText mi
|
let v' = E.encodeUtf8 $ intToText mj <> "." <> intToText mi
|
||||||
|
|
||||||
bindir <- liftIO ghcupBinDir
|
bindir <- liftIO ghcupBinDir
|
||||||
|
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
let myfiles = filter (\x -> ([s|-|] <> v') `B.isSuffixOf` toFilePath x) files
|
let myfiles = filter (\x -> ("-" <> v') `B.isSuffixOf` toFilePath x) files
|
||||||
forM_ myfiles $ \f -> do
|
forM_ myfiles $ \f -> do
|
||||||
let fullF = (bindir </> f)
|
let fullF = (bindir </> f)
|
||||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
@@ -157,7 +159,7 @@ ghcSrcInstalled ver = do
|
|||||||
|
|
||||||
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|]) <$> liftIO ghcupBinDir
|
||||||
|
|
||||||
-- link destination is of the form ../ghc/<ver>/bin/ghc
|
-- link destination is of the form ../ghc/<ver>/bin/ghc
|
||||||
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
||||||
@@ -172,8 +174,8 @@ cabalInstalled ver = do
|
|||||||
|
|
||||||
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
||||||
cabalSet = do
|
cabalSet = do
|
||||||
cabalbin <- (</> ([rel|cabal|] :: Path Rel)) <$> liftIO ghcupBinDir
|
cabalbin <- (</> [rel|cabal|]) <$> liftIO ghcupBinDir
|
||||||
mc <- liftIO $ executeOut cabalbin [[s|--numeric-version|]] Nothing
|
mc <- liftIO $ executeOut cabalbin ["--numeric-version"] Nothing
|
||||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ mc
|
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ mc
|
||||||
case version (E.decodeUtf8 reportedVer) of
|
case version (E.decodeUtf8 reportedVer) of
|
||||||
Left e -> throwM e
|
Left e -> throwM e
|
||||||
@@ -235,15 +237,15 @@ unpackToDir dest av = do
|
|||||||
|
|
||||||
-- extract, depending on file extension
|
-- extract, depending on file extension
|
||||||
if
|
if
|
||||||
| [s|.tar.gz|] `B.isSuffixOf` fn -> liftIO
|
| ".tar.gz" `B.isSuffixOf` fn -> liftIO
|
||||||
(untar . GZip.decompress =<< readFile av)
|
(untar . GZip.decompress =<< readFile av)
|
||||||
| [s|.tar.xz|] `B.isSuffixOf` fn -> do
|
| ".tar.xz" `B.isSuffixOf` fn -> do
|
||||||
filecontents <- liftIO $ readFile av
|
filecontents <- liftIO $ readFile av
|
||||||
let decompressed = Lzma.decompress filecontents
|
let decompressed = Lzma.decompress filecontents
|
||||||
liftIO $ untar decompressed
|
liftIO $ untar decompressed
|
||||||
| [s|.tar.bz2|] `B.isSuffixOf` fn -> liftIO
|
| ".tar.bz2" `B.isSuffixOf` fn -> liftIO
|
||||||
(untar . BZip.decompress =<< readFile av)
|
(untar . BZip.decompress =<< readFile av)
|
||||||
| [s|.tar|] `B.isSuffixOf` fn -> liftIO (untar =<< readFile av)
|
| ".tar" `B.isSuffixOf` fn -> liftIO (untar =<< readFile av)
|
||||||
| otherwise -> throwE $ UnknownArchive fn
|
| otherwise -> throwE $ UnknownArchive fn
|
||||||
|
|
||||||
|
|
||||||
@@ -277,9 +279,6 @@ getRecommended av tool = headOf folded $ getTagged av tool Recommended
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
||||||
getUrlSource :: MonadReader Settings m => m URLSource
|
|
||||||
getUrlSource = ask <&> urlSource
|
|
||||||
|
|
||||||
getCache :: MonadReader Settings m => m Bool
|
getCache :: MonadReader Settings m => m Bool
|
||||||
getCache = ask <&> cache
|
getCache = ask <&> cache
|
||||||
|
|
||||||
@@ -316,7 +315,7 @@ ghcToolFiles ver = do
|
|||||||
-- 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 "ghc-" . takeFileName)
|
||||||
<$> (liftIO $ readSymbolicLink $ toFilePath (bindir </> [rel|ghc|]))
|
<$> (liftIO $ readSymbolicLink $ toFilePath (bindir </> [rel|ghc|]))
|
||||||
when (B.null symver)
|
when (B.null symver)
|
||||||
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
||||||
@@ -328,3 +327,12 @@ ghcToolFiles ver = do
|
|||||||
-- this GHC was built from source. It contains the build config.
|
-- this GHC was built from source. It contains the build config.
|
||||||
ghcUpSrcBuiltFile :: Path Rel
|
ghcUpSrcBuiltFile :: Path Rel
|
||||||
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
||||||
|
|
||||||
|
|
||||||
|
-- | Calls gmake if it exists in PATH, otherwise make.
|
||||||
|
make :: [ByteString] -> Maybe (Path Abs) -> IO (Either ProcessError ())
|
||||||
|
make args workdir = do
|
||||||
|
spaths <- catMaybes . fmap parseAbs <$> getSearchPath
|
||||||
|
has_gmake <- isJust <$> searchPath spaths [rel|gmake|]
|
||||||
|
let mymake = if has_gmake then "gmake" else "make"
|
||||||
|
execLogged mymake True args [rel|ghc-make|] workdir Nothing
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
module GHCup.Utils.Dirs where
|
module GHCup.Utils.Dirs where
|
||||||
@@ -5,7 +6,6 @@ module GHCup.Utils.Dirs where
|
|||||||
|
|
||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -39,14 +39,14 @@ import qualified System.Posix.User as PU
|
|||||||
|
|
||||||
ghcupBaseDir :: IO (Path Abs)
|
ghcupBaseDir :: IO (Path Abs)
|
||||||
ghcupBaseDir = do
|
ghcupBaseDir = do
|
||||||
getEnv [s|GHCUP_INSTALL_BASE_PREFIX|] >>= \case
|
getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> parseAbs r
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
home <- liftIO getHomeDirectory
|
home <- liftIO getHomeDirectory
|
||||||
pure (home </> ([rel|.ghcup|] :: Path Rel))
|
pure (home </> [rel|.ghcup|])
|
||||||
|
|
||||||
ghcupGHCBaseDir :: IO (Path Abs)
|
ghcupGHCBaseDir :: IO (Path Abs)
|
||||||
ghcupGHCBaseDir = ghcupBaseDir <&> (</> ([rel|ghc|] :: Path Rel))
|
ghcupGHCBaseDir = ghcupBaseDir <&> (</> [rel|ghc|])
|
||||||
|
|
||||||
ghcupGHCDir :: Version -> IO (Path Abs)
|
ghcupGHCDir :: Version -> IO (Path Abs)
|
||||||
ghcupGHCDir ver = do
|
ghcupGHCDir ver = do
|
||||||
@@ -56,19 +56,19 @@ ghcupGHCDir ver = do
|
|||||||
|
|
||||||
|
|
||||||
ghcupBinDir :: IO (Path Abs)
|
ghcupBinDir :: IO (Path Abs)
|
||||||
ghcupBinDir = ghcupBaseDir <&> (</> ([rel|bin|] :: Path Rel))
|
ghcupBinDir = ghcupBaseDir <&> (</> [rel|bin|])
|
||||||
|
|
||||||
ghcupCacheDir :: IO (Path Abs)
|
ghcupCacheDir :: IO (Path Abs)
|
||||||
ghcupCacheDir = ghcupBaseDir <&> (</> ([rel|cache|] :: Path Rel))
|
ghcupCacheDir = ghcupBaseDir <&> (</> [rel|cache|])
|
||||||
|
|
||||||
ghcupLogsDir :: IO (Path Abs)
|
ghcupLogsDir :: IO (Path Abs)
|
||||||
ghcupLogsDir = ghcupBaseDir <&> (</> ([rel|logs|] :: Path Rel))
|
ghcupLogsDir = ghcupBaseDir <&> (</> [rel|logs|])
|
||||||
|
|
||||||
|
|
||||||
mkGhcupTmpDir :: (MonadThrow m, MonadIO m) => m (Path Abs)
|
mkGhcupTmpDir :: (MonadThrow m, MonadIO m) => m (Path Abs)
|
||||||
mkGhcupTmpDir = do
|
mkGhcupTmpDir = do
|
||||||
tmpdir <- liftIO $ getEnvDefault [s|TMPDIR|] [s|/tmp|]
|
tmpdir <- liftIO $ getEnvDefault "TMPDIR" "/tmp"
|
||||||
tmp <- liftIO $ mkdtemp $ (tmpdir FP.</> [s|ghcup-|])
|
tmp <- liftIO $ mkdtemp $ (tmpdir FP.</> "ghcup-")
|
||||||
parseAbs tmp
|
parseAbs tmp
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ withGHCupTmpDir = snd <$> allocate mkGhcupTmpDir deleteDirRecursive
|
|||||||
|
|
||||||
getHomeDirectory :: IO (Path Abs)
|
getHomeDirectory :: IO (Path Abs)
|
||||||
getHomeDirectory = do
|
getHomeDirectory = do
|
||||||
e <- getEnv [s|HOME|]
|
e <- getEnv "HOME"
|
||||||
case e of
|
case e of
|
||||||
Just fp -> parseAbs fp
|
Just fp -> parseAbs fp
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
module GHCup.Utils.File where
|
module GHCup.Utils.File where
|
||||||
|
|
||||||
import GHCup.Utils.Dirs
|
import GHCup.Utils.Dirs
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
|
import GHCup.Utils.String.QQ
|
||||||
|
|
||||||
|
import Control.Concurrent
|
||||||
|
import Control.Concurrent.MVar
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.ByteString
|
import Data.ByteString ( ByteString )
|
||||||
import Data.ByteString.Unsafe ( unsafeUseAsCStringLen )
|
import Data.ByteString.Unsafe ( unsafeUseAsCStringLen )
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Functor
|
import Data.Functor
|
||||||
|
import Data.IORef
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.Word8
|
||||||
|
import GHC.Conc hiding ( threadWaitRead )
|
||||||
import GHC.Foreign ( peekCStringLen )
|
import GHC.Foreign ( peekCStringLen )
|
||||||
import GHC.IO.Encoding ( getLocaleEncoding )
|
import GHC.IO.Encoding ( getLocaleEncoding )
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
@@ -23,7 +30,11 @@ import Optics
|
|||||||
import Streamly
|
import Streamly
|
||||||
import Streamly.External.ByteString
|
import Streamly.External.ByteString
|
||||||
import Streamly.External.ByteString.Lazy
|
import Streamly.External.ByteString.Lazy
|
||||||
|
import System.Console.Concurrent
|
||||||
|
import System.Console.Pretty
|
||||||
|
import System.Console.Regions
|
||||||
import System.IO
|
import System.IO
|
||||||
|
import System.IO.Error
|
||||||
import System.Posix.Directory.ByteString
|
import System.Posix.Directory.ByteString
|
||||||
import System.Posix.FD as FD
|
import System.Posix.FD as FD
|
||||||
import System.Posix.FilePath hiding ( (</>) )
|
import System.Posix.FilePath hiding ( (</>) )
|
||||||
@@ -34,6 +45,9 @@ import System.Posix.Process ( ProcessStatus(..) )
|
|||||||
import System.Posix.Types
|
import System.Posix.Types
|
||||||
|
|
||||||
|
|
||||||
|
import qualified Control.Exception as EX
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.Posix.Process.ByteString
|
import qualified System.Posix.Process.ByteString
|
||||||
as SPPB
|
as SPPB
|
||||||
import Streamly.External.Posix.DirStream
|
import Streamly.External.Posix.DirStream
|
||||||
@@ -42,7 +56,16 @@ import qualified Streamly.Internal.Memory.ArrayStream
|
|||||||
import qualified Streamly.FileSystem.Handle as FH
|
import qualified Streamly.FileSystem.Handle as FH
|
||||||
import qualified Streamly.Internal.Data.Unfold as SU
|
import qualified Streamly.Internal.Data.Unfold as SU
|
||||||
import qualified Streamly.Prelude as S
|
import qualified Streamly.Prelude as S
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
||||||
|
as SPIB
|
||||||
|
|
||||||
|
|
||||||
|
data StopThread = StopThread Bool
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
instance Exception StopThread
|
||||||
|
|
||||||
|
|
||||||
data ProcessError = NonZeroExit Int ByteString [ByteString]
|
data ProcessError = NonZeroExit Int ByteString [ByteString]
|
||||||
@@ -99,7 +122,7 @@ findExecutable ex = do
|
|||||||
|
|
||||||
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
||||||
-- The command is run in a subprocess.
|
-- The command is run in a subprocess.
|
||||||
executeOut :: Path b -- ^ command as filename, e.g. 'ls'
|
executeOut :: Path b -- ^ command as filename, e.g. 'ls'
|
||||||
-> [ByteString] -- ^ arguments to the command
|
-> [ByteString] -- ^ arguments to the command
|
||||||
-> Maybe (Path Abs) -- ^ chdir to this path
|
-> Maybe (Path Abs) -- ^ chdir to this path
|
||||||
-> IO CapturedProcess
|
-> IO CapturedProcess
|
||||||
@@ -116,26 +139,101 @@ execLogged :: ByteString -- ^ thing to execute
|
|||||||
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
||||||
-> IO (Either ProcessError ())
|
-> IO (Either ProcessError ())
|
||||||
execLogged exe spath args lfile chdir env = do
|
execLogged exe spath args lfile chdir env = do
|
||||||
ldir <- ghcupLogsDir
|
ldir <- ghcupLogsDir
|
||||||
let logfile = ldir </> lfile
|
logfile <- (ldir </>) <$> parseRel (toFilePath lfile <> ".log")
|
||||||
bracket (createFile (toFilePath logfile) newFilePerms) closeFd action
|
bracket (createFile (toFilePath logfile) newFilePerms) closeFd action
|
||||||
where
|
where
|
||||||
action fd = do
|
action fd = do
|
||||||
pid <- SPPB.forkProcess $ do
|
actionWithPipes $ \(stdoutRead, stdoutWrite) -> do
|
||||||
-- dup stdout
|
-- start the thread that logs to stdout in a region
|
||||||
void $ dupTo fd stdOutput
|
done <- newEmptyMVar
|
||||||
|
tid <-
|
||||||
|
forkIO
|
||||||
|
$ EX.handle (\(e :: StopThread) -> pure ())
|
||||||
|
$ EX.handle (\(e :: IOException) -> pure ())
|
||||||
|
$ flip finally (putMVar done ())
|
||||||
|
$ printToRegion fd stdoutRead 6
|
||||||
|
|
||||||
-- dup stderr
|
-- fork our subprocess
|
||||||
void $ dupTo fd stdError
|
pid <- SPPB.forkProcess $ do
|
||||||
|
void $ dupTo stdoutWrite stdOutput
|
||||||
|
void $ dupTo stdoutWrite stdError
|
||||||
|
closeFd stdoutWrite
|
||||||
|
closeFd stdoutRead
|
||||||
|
|
||||||
-- execute the action
|
-- execute the action
|
||||||
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
||||||
SPPB.executeFile exe spath args env
|
SPPB.executeFile exe spath args env
|
||||||
|
|
||||||
|
closeFd stdoutWrite
|
||||||
|
|
||||||
|
-- wait for the subprocess to finish
|
||||||
|
e <- SPPB.getProcessStatus True True pid >>= \case
|
||||||
|
i@(Just (SPPB.Exited _)) -> pure $ toProcessError exe args i
|
||||||
|
i -> pure $ toProcessError exe args i
|
||||||
|
|
||||||
|
-- make sure the logging thread stops
|
||||||
|
case e of
|
||||||
|
Left _ -> EX.throwTo tid (StopThread False)
|
||||||
|
Right _ -> EX.throwTo tid (StopThread True)
|
||||||
|
takeMVar done
|
||||||
|
|
||||||
|
closeFd stdoutRead
|
||||||
|
pure e
|
||||||
|
|
||||||
|
-- Reads fdIn and logs the output in a continous scrolling area
|
||||||
|
-- of 'size' terminal lines. Also writes to a log file.
|
||||||
|
printToRegion fileFd fdIn size = do
|
||||||
|
ref <- newIORef ([] :: [ByteString])
|
||||||
|
displayConsoleRegions $ do
|
||||||
|
rs <- sequence . replicate size . openConsoleRegion $ Linear
|
||||||
|
EX.handle
|
||||||
|
(\(StopThread b) -> do
|
||||||
|
when b (forM_ rs closeConsoleRegion)
|
||||||
|
EX.throw (StopThread b)
|
||||||
|
)
|
||||||
|
$ readForever
|
||||||
|
(\bs -> do
|
||||||
|
modifyIORef' ref (swapRegs bs)
|
||||||
|
regs <- readIORef ref
|
||||||
|
forM (zip regs rs) $ \(bs, r) -> do
|
||||||
|
setConsoleRegion r $ do
|
||||||
|
w <- consoleWidth
|
||||||
|
return
|
||||||
|
. T.pack
|
||||||
|
. color Blue
|
||||||
|
. T.unpack
|
||||||
|
. E.decodeUtf8
|
||||||
|
. trim w
|
||||||
|
. (\b -> "[ " <> toFilePath lfile <> " ] " <> b)
|
||||||
|
$ bs
|
||||||
|
SPIB.fdWrite fileFd (bs <> "\n")
|
||||||
|
)
|
||||||
|
fdIn
|
||||||
|
|
||||||
|
|
||||||
SPPB.getProcessStatus True True pid >>= \case
|
where
|
||||||
i@(Just (SPPB.Exited _)) -> pure $ toProcessError exe args i
|
swapRegs bs regs | length regs < size = regs ++ [bs]
|
||||||
i -> pure $ toProcessError exe args i
|
| otherwise = tail regs ++ [bs]
|
||||||
|
|
||||||
|
-- trim output line to terminal width
|
||||||
|
trim w bs | BS.length bs > w && w > 5 = BS.take (w - 4) bs <> "..."
|
||||||
|
| otherwise = bs
|
||||||
|
|
||||||
|
-- read an entire line from the file descriptor (removes the newline char)
|
||||||
|
readLine fd' = do
|
||||||
|
threadWaitRead fd'
|
||||||
|
bs <- SPIB.fdRead fd' 1
|
||||||
|
if
|
||||||
|
| bs == "\n" -> pure ""
|
||||||
|
| bs == "" -> pure ""
|
||||||
|
| otherwise -> fmap (bs <>) $ readLine fd'
|
||||||
|
|
||||||
|
readForever action' fd' = do
|
||||||
|
bs <- readLine fd'
|
||||||
|
if not $ BS.null bs
|
||||||
|
then action' bs >> readForever action' fd'
|
||||||
|
else readForever action' fd'
|
||||||
|
|
||||||
|
|
||||||
-- | Capture the stdout and stderr of the given action, which
|
-- | Capture the stdout and stderr of the given action, which
|
||||||
@@ -176,10 +274,13 @@ captureOutStreams action =
|
|||||||
}
|
}
|
||||||
_ -> throwIO $ userError $ ("No such PID " ++ show pid)
|
_ -> throwIO $ userError $ ("No such PID " ++ show pid)
|
||||||
|
|
||||||
where
|
|
||||||
actionWithPipes a =
|
actionWithPipes :: ((Fd, Fd) -> IO b) -> IO b
|
||||||
createPipe >>= \(p1, p2) -> (flip finally) (cleanup [p1, p2]) $ a (p1, p2)
|
actionWithPipes a =
|
||||||
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
createPipe >>= \(p1, p2) -> (flip finally) (cleanup [p1, p2]) $ a (p1, p2)
|
||||||
|
|
||||||
|
cleanup :: [Fd] -> IO ()
|
||||||
|
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ import GHCup.Utils.Version.QQ
|
|||||||
import Data.Versions
|
import Data.Versions
|
||||||
|
|
||||||
ghcUpVer :: PVP
|
ghcUpVer :: PVP
|
||||||
ghcUpVer = [pver|0.1.0|]
|
ghcUpVer = [pver|0.0.0|]
|
||||||
|
|||||||
39
stack.yaml
Normal file
39
stack.yaml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
resolver: lts-14.27
|
||||||
|
packages:
|
||||||
|
- .
|
||||||
|
extra-deps:
|
||||||
|
- IfElse-0.85@sha256:6939b94acc6a55f545f63a168a349dd2fbe4b9a7cca73bf60282db5cc6aa47d2
|
||||||
|
- ascii-string-1.0.1.3
|
||||||
|
- brotli-0.0.0.0@sha256:448061ceabdcaa752bbaf208f255bbb7e90bbcf8ea8a913d26ffa7887636823b
|
||||||
|
- brotli-streams-0.0.0.0@sha256:c75a1d5d33420cbc9399c315e9b50a1976a5370f4fa8a40c71e11d011c2fedd6
|
||||||
|
- case-insensitive-1.2.1.0
|
||||||
|
- data-default-instances-base-0.1.0.1@sha256:985a13d7103e45a65f06f277b735ef025636014f0d29dd6de998bc7628e09be9
|
||||||
|
- fusion-plugin-types-0.1.0@sha256:0f11bbc445ab8ae3dbbb3d5d2ea198bdb1ac020518b7f4f7579035dc89182438
|
||||||
|
- generics-sop-0.5.0.0
|
||||||
|
- haskus-utils-data-1.2@sha256:48f62aa23d84b94edd0338379d3b3d74a34d3c2dbabf8c448a774a89ca70ea5d
|
||||||
|
- haskus-utils-types-1.5
|
||||||
|
- haskus-utils-variant-3.0
|
||||||
|
- hpath-0.11.0
|
||||||
|
- hpath-directory-0.13.2
|
||||||
|
- hpath-filepath-0.10.4
|
||||||
|
- hpath-io-0.13.1
|
||||||
|
- hpath-posix-0.13.1
|
||||||
|
- http-io-streams-0.1.2.0
|
||||||
|
- indexed-profunctors-0.1@sha256:ddf618d0d4c58319c1e735e746bc69a1021f13b6f475dc9614b80af03432e6d4
|
||||||
|
- language-bash-0.9.0
|
||||||
|
- optics-0.2
|
||||||
|
- optics-core-0.2@sha256:cfdf39871553769b59fcc54863a3521d262ea25d8d05d0f41ab87296c560cfa6
|
||||||
|
- optics-extra-0.2@sha256:211ce1dfd1b3ffd95c1158d8c8beb53cbd17c4d477169e226b1831607f6789eb
|
||||||
|
- optics-th-0.2@sha256:b4f6c5a3f134c697837190ed6da969dd284aefe79e43c3ef793093b607bb83b9
|
||||||
|
- optics-vl-0.2
|
||||||
|
- optparse-applicative-0.15.1.0
|
||||||
|
- pretty-terminal-0.1.0.0
|
||||||
|
- sop-core-0.5.0.0@sha256:8734ab38b8c84837094eec657da0b58942e481e20166131f34cf6c7fe9787b07
|
||||||
|
- streamly-0.7.1
|
||||||
|
- streamly-bytestring-0.1.2
|
||||||
|
- streamly-posix-0.1.0.0
|
||||||
|
- strict-base-0.4.0.0
|
||||||
|
- string-interpolate-0.2.0.0
|
||||||
|
- table-layout-0.8.0.5
|
||||||
|
- tar-bytestring-0.6.3.0
|
||||||
|
- time-1.9.3
|
||||||
Reference in New Issue
Block a user