Compare commits
74 Commits
v0.1.8-ghc
...
v0.1.11-rc
| Author | SHA1 | Date | |
|---|---|---|---|
| 5da5fabfef | |||
| 05cc55c52d | |||
| 571df1349c | |||
| cbbb75062c | |||
| bb7c4205db | |||
| b2027f1625 | |||
| 65945c87df | |||
| 081582d3e1 | |||
| bf240af518 | |||
| a269131e2d | |||
| 59ece98fdc | |||
| 563924ff26 | |||
| 8ee3f55428 | |||
| 93c17607b5 | |||
| 8b4c239444 | |||
| 8bef17bf59 | |||
| a649146a39 | |||
| 9d6a5313ab | |||
| de09c950d5 | |||
| 47838b1bd9 | |||
| 02b360e2a9 | |||
| c10ab15e0c | |||
| 46f3da1a94 | |||
| 7ec9d90aab | |||
| 326bf510c9 | |||
| ce3d1f4309 | |||
| b31ba883e4 | |||
| e5d1c04616 | |||
| 34ff0ed9cf | |||
| 85bd87d5f3 | |||
| 8b274214af | |||
| 069e3102f4 | |||
| 8623b32721 | |||
| 6342e8edf0 | |||
| bbd8f0c84c | |||
| 873c951d6e | |||
| d9c864d3c5 | |||
| 4280d7109a | |||
| c8855c068f | |||
| 90503061e9 | |||
| 672ebf6426 | |||
| fd76fde23a | |||
| e24c9a3ffe | |||
| 2641d50c21 | |||
| 202f3ea3ba | |||
| 4f09e3ff7e | |||
| 1148219130 | |||
| 4b47800dfb | |||
| e2c4db9132 | |||
| 90af68b211 | |||
| 80603662b4 | |||
| d2c5d4dfd9 | |||
| 6f1b8b4041 | |||
| f63b2bf744 | |||
| 71cb75c170 | |||
| dac64f5718 | |||
| 27b2d2ac7d | |||
| 47142dd376 | |||
| d071a7e51b | |||
| 5c45884f5f | |||
| cafedd73a2 | |||
| 7163b77837 | |||
| 122c54b51e | |||
| b9d7d7d007 | |||
| 9050c9792a | |||
| aac8f760ad | |||
| 7d334c18f5 | |||
| 86b0e4b31b | |||
| af811f3dbc | |||
| d30d2ac8a5 | |||
| 86a4b10de6 | |||
| cfa7049ab9 | |||
| 391676e90a | |||
| a046f16308 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ tags
|
||||
TAGS
|
||||
/tmp/
|
||||
.entangled
|
||||
release/
|
||||
|
||||
@@ -14,9 +14,10 @@ variables:
|
||||
- x86_64-linux
|
||||
variables:
|
||||
OS: "LINUX"
|
||||
BIT: "64"
|
||||
|
||||
.alpine:64bit:
|
||||
image: "alpine:edge"
|
||||
image: "alpine:3.12"
|
||||
tags:
|
||||
- x86_64-linux
|
||||
variables:
|
||||
@@ -24,7 +25,7 @@ variables:
|
||||
BIT: "64"
|
||||
|
||||
.alpine:32bit:
|
||||
image: "i386/alpine:edge"
|
||||
image: "i386/alpine:3.12"
|
||||
tags:
|
||||
- x86_64-linux
|
||||
variables:
|
||||
@@ -36,12 +37,14 @@ variables:
|
||||
- x86_64-darwin
|
||||
variables:
|
||||
OS: "DARWIN"
|
||||
BIT: "64"
|
||||
|
||||
.freebsd:
|
||||
tags:
|
||||
- x86_64-freebsd
|
||||
variables:
|
||||
OS: "FREEBSD"
|
||||
BIT: "64"
|
||||
|
||||
.root_cleanup:
|
||||
after_script:
|
||||
@@ -66,6 +69,13 @@ variables:
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/install_deps.sh
|
||||
|
||||
.test_ghcup_version:linux32:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
- .alpine:32bit
|
||||
before_script:
|
||||
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
||||
|
||||
.test_ghcup_version:darwin:
|
||||
extends:
|
||||
- .test_ghcup_version
|
||||
@@ -103,10 +113,17 @@ test:linux:recommended:
|
||||
test:linux:latest:
|
||||
extends: .test_ghcup_version:linux
|
||||
variables:
|
||||
GHC_VERSION: "8.10.1"
|
||||
GHC_VERSION: "8.10.2"
|
||||
CABAL_VERSION: "3.2.0.0"
|
||||
allow_failure: true
|
||||
|
||||
######## linux 32bit test ########
|
||||
|
||||
test:linux:recommended:32bit:
|
||||
extends: .test_ghcup_version:linux32
|
||||
variables:
|
||||
GHC_VERSION: "8.8.4"
|
||||
CABAL_VERSION: "3.2.0.0"
|
||||
|
||||
######## darwin test ########
|
||||
|
||||
@@ -119,7 +136,7 @@ test:mac:recommended:
|
||||
test:mac:latest:
|
||||
extends: .test_ghcup_version:darwin
|
||||
variables:
|
||||
GHC_VERSION: "8.10.1"
|
||||
GHC_VERSION: "8.10.2"
|
||||
CABAL_VERSION: "3.2.0.0"
|
||||
allow_failure: true
|
||||
|
||||
@@ -135,7 +152,7 @@ test:freebsd:recommended:
|
||||
test:freebsd:latest:
|
||||
extends: .test_ghcup_version:freebsd
|
||||
variables:
|
||||
GHC_VERSION: "8.10.1"
|
||||
GHC_VERSION: "8.10.2"
|
||||
CABAL_VERSION: "3.2.0.0"
|
||||
allow_failure: true
|
||||
|
||||
|
||||
@@ -18,10 +18,6 @@ apk add --no-cache \
|
||||
tar \
|
||||
perl
|
||||
|
||||
ln -sf libncurses.so /usr/lib/libtinfo.so
|
||||
ln -sf libncursesw.so.6 /usr/lib/libtinfow.so.6
|
||||
ln -sf libtinfow.so.6 /usr/lib/libtinfow.so
|
||||
|
||||
if [ "${BIT}" = "32" ] ; then
|
||||
curl -sSfL https://downloads.haskell.org/ghcup/i386-linux-ghcup > ./ghcup-bin
|
||||
else
|
||||
@@ -49,5 +45,3 @@ apk add --no-cache \
|
||||
xz-dev \
|
||||
ncurses-static
|
||||
|
||||
ln -sf libncursesw.a /usr/lib/libtinfow.a
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ ecabal() {
|
||||
}
|
||||
|
||||
eghcup() {
|
||||
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.json "$@"
|
||||
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.yaml "$@"
|
||||
}
|
||||
|
||||
git describe --always
|
||||
@@ -20,16 +20,28 @@ git describe --always
|
||||
|
||||
ecabal update
|
||||
|
||||
ecabal install -w ghc-${GHC_VERSION} --installdir="$CI_PROJECT_DIR"/.local/bin hspec-discover
|
||||
|
||||
if [ "${OS}" = "DARWIN" ] ; then
|
||||
ecabal build -w ghc-${GHC_VERSION} -ftui
|
||||
ecabal test -w ghc-${GHC_VERSION} -ftui ghcup-test
|
||||
elif [ "${OS}" = "LINUX" ] ; then
|
||||
if [ "${BIT}" = "32" ] ; then
|
||||
ecabal build -w ghc-${GHC_VERSION} -finternal-downloader -ftui -ftar
|
||||
ecabal test -w ghc-${GHC_VERSION} -finternal-downloader -ftui -ftar ghcup-test
|
||||
else
|
||||
ecabal build -w ghc-${GHC_VERSION} -finternal-downloader -ftui
|
||||
ecabal test -w ghc-${GHC_VERSION} -finternal-downloader -ftui ghcup-test
|
||||
fi
|
||||
else
|
||||
ecabal build -w ghc-${GHC_VERSION} -finternal-downloader -ftui
|
||||
ecabal test -w ghc-${GHC_VERSION} -finternal-downloader -ftui ghcup-test
|
||||
fi
|
||||
|
||||
ecabal haddock
|
||||
ecabal haddock -w ghc-${GHC_VERSION} -ftar
|
||||
|
||||
cp "$(ecabal new-exec --enable-tests --verbose=0 --offline sh -- -c 'command -v ghcup')" .
|
||||
cp "$(ecabal new-exec --enable-tests --verbose=0 --offline sh -- -c 'command -v ghcup-gen')" .
|
||||
cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup')" .
|
||||
cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup-gen')" .
|
||||
|
||||
cp ./ghcup "$CI_PROJECT_DIR"/.local/bin/ghcup
|
||||
cp ./ghcup-gen "$CI_PROJECT_DIR"/.local/bin/ghcup-gen
|
||||
@@ -42,7 +54,7 @@ rm -rf "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup
|
||||
### manual cli based testing
|
||||
|
||||
|
||||
ghcup-gen check -f ghcup-${JSON_VERSION}.json
|
||||
ghcup-gen check -f ghcup-${JSON_VERSION}.yaml
|
||||
|
||||
eghcup --numeric-version
|
||||
|
||||
@@ -81,6 +93,18 @@ eghcup set ${GHC_VERSION}
|
||||
eghcup rm 8.4.4
|
||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||
|
||||
# install hls
|
||||
if [ "${OS}" = "DARWIN" ] ; then
|
||||
eghcup install hls
|
||||
haskell-language-server-wrapper --version
|
||||
elif [ "${OS}" = "LINUX" ] ; then
|
||||
if [ "${BIT}" = "64" ] ; then
|
||||
eghcup install hls
|
||||
haskell-language-server-wrapper --version
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
eghcup rm $(ghc --numeric-version)
|
||||
|
||||
eghcup upgrade
|
||||
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,5 +1,32 @@
|
||||
# Revision history for ghcup
|
||||
|
||||
## 0.1.11 -- ????-??-??
|
||||
|
||||
* Add support for installing haskell-language-server, wrt #65
|
||||
* When compiling GHC from source create a bindist first, store that bindist in `~/.ghcup/cache` and install it, wrt #51
|
||||
* Allow to compile over existing version (`ghcup compile ghc -v 8.6.5 -b 8.6.5`) and replace it wrt #59
|
||||
* simplify installing from custom bindist wrt #60
|
||||
- `ghcup install ghc -u <url> <version>`
|
||||
* fix bug when cabal isn't marked executable in bindist
|
||||
* fix bug when `~/.ghcup` is a valid symlink wrt #49
|
||||
* Add JSON roundtrip tests
|
||||
|
||||
## 0.1.10 -- 2020-08-14
|
||||
|
||||
* Show stray Cabals (useful for pre-releases or compiled ones)
|
||||
|
||||
## 0.1.9 -- 2020-08-14
|
||||
|
||||
* Fix bug when uninstalling all cabal versions
|
||||
* Fix bug when setting a non-installed ghc version as current default
|
||||
* Use yaml instead of generated json for download info for ease of adding new GHC versions #44
|
||||
* Allow pre-release versions of GHC/cabal
|
||||
* Add XDG dirs support (set `GHCUP_USE_XDG_DIRS`) wrt #39
|
||||
* Allow to specify regex for tarball subdir (e.g. `ghc-.*`)
|
||||
* Allow installing arbitrary bindists more seamlessly:
|
||||
- e.g. installing GHC HEAD: `ghcup -n install ghc -u '{"dlHash": "", "dlSubdir": { "RegexDir": "ghc-.*"}, "dlUri": "https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27" }' head`
|
||||
* Avoid duplicate edits to .bashrc/.zshrc wrt #43
|
||||
|
||||
## 0.1.8 -- 2020-07-21
|
||||
|
||||
* Fix bug in logging thread dying on newlines
|
||||
|
||||
35
README.md
35
README.md
@@ -9,10 +9,15 @@ Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [p
|
||||
## Table of Contents
|
||||
|
||||
* [Installation](#installation)
|
||||
* [Simple bootstrap](#simple-bootstrap)
|
||||
* [Manual install](#manual-install)
|
||||
* [Vim integration](#vim-integration)
|
||||
* [Usage](#usage)
|
||||
* [Manpages](#manpages)
|
||||
* [Shell-completion](#shell-completion)
|
||||
* [Cross support](#cross-support)
|
||||
* [XDG support](#xdg-support)
|
||||
* [Installing custom bindists](#installing-custom-bindists)
|
||||
* [Design goals](#design-goals)
|
||||
* [How](#how)
|
||||
* [Known users](#known-users)
|
||||
@@ -36,6 +41,10 @@ Then adjust your `PATH` in `~/.bashrc` (or similar, depending on your shell) lik
|
||||
export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
|
||||
```
|
||||
|
||||
### Vim integration
|
||||
|
||||
See [ghcup.vim](https://github.com/hasufell/ghcup.vim).
|
||||
|
||||
## Usage
|
||||
|
||||
See `ghcup --help`.
|
||||
@@ -96,6 +105,32 @@ For distributions with non-standard locations of cross toolchain and
|
||||
libraries, this may need some tweaking of `build.mk` or configure args.
|
||||
See `ghcup compile ghc --help` for further information.
|
||||
|
||||
### XDG support
|
||||
|
||||
To enable XDG style directories, set the environment variable `GHCUP_USE_XDG_DIRS` to anything.
|
||||
|
||||
Then you can control the locations via XDG environment variables as such:
|
||||
|
||||
* `XDG_DATA_HOME`: GHCs will be unpacked in `ghcup/ghc` subdir
|
||||
* `XDG_CACHE_HOME`: logs and download files will be stored in `ghcup` subdir
|
||||
* `XDG_BIN_HOME`: binaries end up here (default: `~/.local/bin`)
|
||||
|
||||
### Installing custom bindists
|
||||
|
||||
There are a couple of good use cases to install custom bindists:
|
||||
|
||||
1. manually built bindists (e.g. with patches)
|
||||
- example: `ghcup install ghc -u 'file:///home/mearwald/tmp/ghc-eff-patches/ghc-8.10.2-x86_64-deb10-linux.tar.xz' 8.10.2-eff`
|
||||
2. GHC head CI bindists
|
||||
- example: `ghcup install ghc -u 'https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27' head`
|
||||
3. DWARF bindists
|
||||
- example: `ghcup install ghc -u 'https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-deb10-linux-dwarf.tar.xz' 8.10.2-dwarf`
|
||||
|
||||
Since the version parser is pretty lax, `8.10.2-eff` and `head` are both valid versions
|
||||
and produce the binaries `ghc-8.10.2-eff` and `ghc-head` respectively.
|
||||
GHCup always needs to know which version the bindist corresponds to (this is not automatically
|
||||
detected).
|
||||
|
||||
## Design goals
|
||||
|
||||
1. simplicity
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# RELEASING
|
||||
|
||||
1. update `GHCup.Version` module. `ghcupURL` must only be updated if we change the `_toolRequirements` type or the JSON representation of it. The version of the json represents the change increments. `ghcUpVer` is the current application version.
|
||||
1. update `GHCup.Version` module. `ghcupURL` must only be updated if we change the `_toolRequirements` type or the YAML representation of it. The version of the YAML represents the change increments. `ghcUpVer` is the current application version.
|
||||
|
||||
2. Update version in ghcup.cabal
|
||||
|
||||
3. Add ChangeLog entry
|
||||
|
||||
4. Add/fix downloads to `GHCupDownloads` module, then run `ghcup-gen gen` to generate the new json and validate it via `ghcup-gen check`.
|
||||
4. Add/fix downloads in `ghcup-<ver>.yaml`, then verify with `ghcup-gen check -f ghcup-<ver>.yaml`
|
||||
|
||||
5. Commit and git push with tag. Wait for tests to succeed and release artifacts to build.
|
||||
|
||||
6. Download release artifacts and upload them `downloads.haskell.org/ghcup`
|
||||
|
||||
7. Add release artifacts to GHCupDownloads (see point 4.)
|
||||
7. Add release artifacts to yaml file (see point 4.)
|
||||
|
||||
8. Upload the final `ghcup-<ver>.json` to `webhost.haskell.org/ghcup/data/`.
|
||||
8. Upload the final `ghcup-<ver>.yaml` to `webhost.haskell.org/ghcup/data/`.
|
||||
|
||||
9. Update bootstrap-haskell and symlinks on `downloads.haskell.org/ghcup`
|
||||
|
||||
@@ -10,13 +10,10 @@
|
||||
|
||||
module Main where
|
||||
|
||||
import GHCup.Data.GHCupInfo
|
||||
import GHCup.Types
|
||||
import GHCup.Types.JSON ( )
|
||||
import GHCup.Utils.Logger
|
||||
|
||||
import Data.Aeson ( eitherDecode, encode )
|
||||
import Data.Aeson.Encode.Pretty
|
||||
#if !MIN_VERSION_base(4,13,0)
|
||||
import Data.Semigroup ( (<>) )
|
||||
#endif
|
||||
@@ -27,48 +24,15 @@ import System.IO ( stdout )
|
||||
import Validate
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.Yaml as Y
|
||||
|
||||
|
||||
data Options = Options
|
||||
{ optCommand :: Command
|
||||
}
|
||||
|
||||
data Command = GenJSON GenJSONOpts
|
||||
| ValidateJSON ValidateJSONOpts
|
||||
| ValidateTarballs ValidateJSONOpts
|
||||
|
||||
data Output
|
||||
= FileOutput FilePath -- optsparse-applicative doesn't handle ByteString correctly anyway
|
||||
| StdOutput
|
||||
|
||||
fileOutput :: Parser Output
|
||||
fileOutput =
|
||||
FileOutput
|
||||
<$> (strOption
|
||||
(long "file" <> short 'f' <> metavar "FILENAME" <> help
|
||||
"Output to a file"
|
||||
)
|
||||
)
|
||||
|
||||
stdOutput :: Parser Output
|
||||
stdOutput = flag'
|
||||
StdOutput
|
||||
(short 'o' <> long "stdout" <> help "Print to stdout (default)")
|
||||
|
||||
outputP :: Parser Output
|
||||
outputP = fileOutput <|> stdOutput
|
||||
|
||||
|
||||
data GenJSONOpts = GenJSONOpts
|
||||
{ output :: Maybe Output
|
||||
, pretty :: Bool
|
||||
}
|
||||
|
||||
genJSONOpts :: Parser GenJSONOpts
|
||||
genJSONOpts = GenJSONOpts <$> optional outputP <*> switch
|
||||
(short 'p' <> long "pretty" <> help "Make JSON output pretty (human readable)"
|
||||
)
|
||||
data Command = ValidateYAML ValidateYAMLOpts
|
||||
| ValidateTarballs ValidateYAMLOpts
|
||||
|
||||
|
||||
data Input
|
||||
@@ -92,12 +56,12 @@ stdInput = flag'
|
||||
inputP :: Parser Input
|
||||
inputP = fileInput <|> stdInput
|
||||
|
||||
data ValidateJSONOpts = ValidateJSONOpts
|
||||
{ input :: Maybe Input
|
||||
data ValidateYAMLOpts = ValidateYAMLOpts
|
||||
{ vInput :: Maybe Input
|
||||
}
|
||||
|
||||
validateJSONOpts :: Parser ValidateJSONOpts
|
||||
validateJSONOpts = ValidateJSONOpts <$> optional inputP
|
||||
validateYAMLOpts :: Parser ValidateYAMLOpts
|
||||
validateYAMLOpts = ValidateYAMLOpts <$> optional inputP
|
||||
|
||||
opts :: Parser Options
|
||||
opts = Options <$> com
|
||||
@@ -105,18 +69,10 @@ opts = Options <$> com
|
||||
com :: Parser Command
|
||||
com = subparser
|
||||
( (command
|
||||
"gen"
|
||||
( GenJSON
|
||||
<$> (info (genJSONOpts <**> helper)
|
||||
(progDesc "Generate the json downloads file")
|
||||
)
|
||||
)
|
||||
)
|
||||
<> (command
|
||||
"check"
|
||||
( ValidateJSON
|
||||
<$> (info (validateJSONOpts <**> helper)
|
||||
(progDesc "Validate the JSON")
|
||||
( ValidateYAML
|
||||
<$> (info (validateYAMLOpts <**> helper)
|
||||
(progDesc "Validate the YAML")
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -124,7 +80,7 @@ com = subparser
|
||||
"check-tarballs"
|
||||
( ValidateTarballs
|
||||
<$> (info
|
||||
(validateJSONOpts <**> helper)
|
||||
(validateYAMLOpts <**> helper)
|
||||
(progDesc "Validate all tarballs (download and checksum)")
|
||||
)
|
||||
)
|
||||
@@ -135,38 +91,27 @@ com = subparser
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
|
||||
_ <- customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
|
||||
>>= \Options {..} -> case optCommand of
|
||||
GenJSON gopts -> do
|
||||
let bs True =
|
||||
encodePretty' (defConfig { confIndent = Spaces 2 }) ghcupInfo
|
||||
bs False = encode ghcupInfo
|
||||
case gopts of
|
||||
GenJSONOpts { output = Nothing, pretty } ->
|
||||
L.hPutStr stdout (bs pretty)
|
||||
GenJSONOpts { output = Just StdOutput, pretty } ->
|
||||
L.hPutStr stdout (bs pretty)
|
||||
GenJSONOpts { output = Just (FileOutput file), pretty } ->
|
||||
L.writeFile file (bs pretty)
|
||||
ValidateJSON vopts -> case vopts of
|
||||
ValidateJSONOpts { input = Nothing } ->
|
||||
L.getContents >>= valAndExit validate
|
||||
ValidateJSONOpts { input = Just StdInput } ->
|
||||
L.getContents >>= valAndExit validate
|
||||
ValidateJSONOpts { input = Just (FileInput file) } ->
|
||||
L.readFile file >>= valAndExit validate
|
||||
ValidateYAML vopts -> case vopts of
|
||||
ValidateYAMLOpts { vInput = Nothing } ->
|
||||
B.getContents >>= valAndExit validate
|
||||
ValidateYAMLOpts { vInput = Just StdInput } ->
|
||||
B.getContents >>= valAndExit validate
|
||||
ValidateYAMLOpts { vInput = Just (FileInput file) } ->
|
||||
B.readFile file >>= valAndExit validate
|
||||
ValidateTarballs vopts -> case vopts of
|
||||
ValidateJSONOpts { input = Nothing } ->
|
||||
L.getContents >>= valAndExit validateTarballs
|
||||
ValidateJSONOpts { input = Just StdInput } ->
|
||||
L.getContents >>= valAndExit validateTarballs
|
||||
ValidateJSONOpts { input = Just (FileInput file) } ->
|
||||
L.readFile file >>= valAndExit validateTarballs
|
||||
ValidateYAMLOpts { vInput = Nothing } ->
|
||||
B.getContents >>= valAndExit validateTarballs
|
||||
ValidateYAMLOpts { vInput = Just StdInput } ->
|
||||
B.getContents >>= valAndExit validateTarballs
|
||||
ValidateYAMLOpts { vInput = Just (FileInput file) } ->
|
||||
B.readFile file >>= valAndExit validateTarballs
|
||||
pure ()
|
||||
|
||||
where
|
||||
valAndExit f contents = do
|
||||
(GHCupInfo _ av) <- case eitherDecode contents of
|
||||
(GHCupInfo _ av) <- case Y.decodeEither' contents of
|
||||
Right r -> pure r
|
||||
Left e -> die (color Red $ show e)
|
||||
myLoggerT (LoggerConfig True (B.hPut stdout) (\_ -> pure ())) (f av)
|
||||
|
||||
@@ -7,7 +7,9 @@ module Validate where
|
||||
import GHCup
|
||||
import GHCup.Download
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.Logger
|
||||
import GHCup.Utils.Version.QQ
|
||||
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
@@ -53,7 +55,7 @@ validate :: (Monad m, MonadLogger m, MonadThrow m, MonadIO m, MonadUnliftIO m)
|
||||
validate dls = do
|
||||
ref <- liftIO $ newIORef 0
|
||||
|
||||
-- * verify binary downloads * --
|
||||
-- verify binary downloads --
|
||||
flip runReaderT ref $ do
|
||||
-- unique tags
|
||||
forM_ (M.toList dls) $ \(t, _) -> checkUniqueTags t
|
||||
@@ -88,6 +90,15 @@ validate dls = do
|
||||
when ((not $ any (== FreeBSD) pspecs) && arch == A_64) $ lift $ $(logWarn)
|
||||
[i|FreeBSD missing for #{t} #{v'} #{arch}|]
|
||||
|
||||
-- alpine needs to be set explicitly, because
|
||||
-- we cannot assume that "Linux UnknownLinux" runs on Alpine
|
||||
-- (although it could be static)
|
||||
when (not $ any (== Linux Alpine) pspecs) $
|
||||
case t of
|
||||
GHCup -> (lift $ $(logError) [i|Linux Alpine missing for #{t} #{v'} #{arch}|]) >> addError
|
||||
Cabal | v > [vver|2.4.1.0|] -> (lift $ $(logError) [i|Linux Alpine missing for #{t} #{v'} #{arch}|]) >> addError
|
||||
_ -> lift $ $(logWarn) [i|Linux Alpine missing for #{t} #{v'} #{arch}|]
|
||||
|
||||
checkUniqueTags tool = do
|
||||
let allTags = join $ M.elems $ availableToolVersions dls tool
|
||||
let nonUnique =
|
||||
@@ -111,6 +122,7 @@ validate dls = do
|
||||
where
|
||||
isUniqueTag Latest = True
|
||||
isUniqueTag Recommended = True
|
||||
isUniqueTag Prerelease = False
|
||||
isUniqueTag (Base _) = False
|
||||
isUniqueTag (UnknownTag _) = False
|
||||
|
||||
@@ -179,7 +191,8 @@ validateTarballs dls = do
|
||||
|
||||
where
|
||||
downloadAll dli = do
|
||||
let settings = Settings True False Never Curl False
|
||||
dirs <- liftIO getDirs
|
||||
let settings = Settings True False Never Curl False dirs
|
||||
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
||||
, colorOutter = B.hPut stderr
|
||||
, rawOutter = (\_ -> pure ())
|
||||
|
||||
@@ -71,19 +71,26 @@ ui AppState {..} =
|
||||
( padBottom Max
|
||||
$ ( withBorderStyle unicode
|
||||
$ borderWithLabel (str "GHCup")
|
||||
$ (center $ renderList renderItem True lr)
|
||||
$ (center $ (header <=> hBorder <=> renderList renderItem True lr))
|
||||
)
|
||||
)
|
||||
<=> ( withAttr "help"
|
||||
. txtWrap
|
||||
. T.pack
|
||||
. foldr1 (\x y -> x <> " " <> y)
|
||||
. (++ ["↑↓:Navigation"])
|
||||
$ (fmap (\(c, s, _) -> (c : ':' : s)) keyHandlers)
|
||||
)
|
||||
<=> footer
|
||||
|
||||
where
|
||||
renderItem b ListResult {..} =
|
||||
footer =
|
||||
withAttr "help"
|
||||
. txtWrap
|
||||
. T.pack
|
||||
. foldr1 (\x y -> x <> " " <> y)
|
||||
. (++ ["↑↓:Navigation"])
|
||||
$ (fmap (\(c, s, _) -> (c : ':' : s)) keyHandlers)
|
||||
header =
|
||||
(minHSize 2 $ emptyWidget)
|
||||
<+> (padLeft (Pad 2) $ minHSize 6 $ str "Tool")
|
||||
<+> (minHSize 15 $ str "Version")
|
||||
<+> (padLeft (Pad 1) $ minHSize 25 $ str "Tags")
|
||||
<+> (padLeft (Pad 5) $ str "Notes")
|
||||
renderItem b listResult@(ListResult {..}) =
|
||||
let marks = if
|
||||
| lSet -> (withAttr "set" $ str "✔✔")
|
||||
| lInstalled -> (withAttr "installed" $ str "✓ ")
|
||||
@@ -94,27 +101,43 @@ ui AppState {..} =
|
||||
dim = if lNoBindist
|
||||
then updateAttrMap (const dimAttributes) . withAttr "no-bindist"
|
||||
else id
|
||||
active = if b then withAttr "active" else id
|
||||
in dim
|
||||
( marks
|
||||
<+> ( padLeft (Pad 2)
|
||||
$ minHSize 20
|
||||
$ ((if b then withAttr "active" else id)
|
||||
(str $ (fmap toLower . show $ lTool) <> " " <> ver)
|
||||
)
|
||||
<+> (( padLeft (Pad 2)
|
||||
$ active
|
||||
$ minHSize 6
|
||||
$ (str (fmap toLower . show $ lTool))
|
||||
)
|
||||
)
|
||||
<+> (padLeft (Pad 1) $ if null lTag
|
||||
<+> (minHSize 15 $ active $ (str ver))
|
||||
<+> (padLeft (Pad 1) $ minHSize 25 $ if null lTag
|
||||
then emptyWidget
|
||||
else
|
||||
foldr1 (\x y -> x <+> str "," <+> y)
|
||||
$ (fmap printTag $ sort lTag)
|
||||
)
|
||||
<+> ( padLeft (Pad 5)
|
||||
$ let notes = printNotes listResult
|
||||
in if null notes
|
||||
then emptyWidget
|
||||
else foldr1 (\x y -> x <+> str "," <+> y) $ notes
|
||||
)
|
||||
)
|
||||
|
||||
printTag Recommended = withAttr "recommended" $ str "recommended"
|
||||
printTag Latest = withAttr "latest" $ str "latest"
|
||||
printTag Prerelease = withAttr "prerelease" $ str "prerelease"
|
||||
printTag (Base pvp'') = str ("base-" ++ T.unpack (prettyPVP pvp''))
|
||||
printTag (UnknownTag t ) = str t
|
||||
|
||||
printNotes ListResult {..} =
|
||||
(if hlsPowered then [withAttr "hls-powered" $ str "hls-powered"] else mempty
|
||||
)
|
||||
++ (if fromSrc then [withAttr "compiled" $ str "compiled"] else mempty)
|
||||
++ (if lStray then [withAttr "stray" $ str "stray"] else mempty)
|
||||
|
||||
|
||||
|
||||
minHSize :: Int -> Widget n -> Widget n
|
||||
minHSize s' = hLimit s' . vLimit 1 . (<+> fill ' ')
|
||||
@@ -136,7 +159,11 @@ defaultAttributes = attrMap
|
||||
, ("set" , Vty.defAttr `Vty.withForeColor` Vty.green)
|
||||
, ("installed" , Vty.defAttr `Vty.withForeColor` Vty.green)
|
||||
, ("recommended" , Vty.defAttr `Vty.withForeColor` Vty.green)
|
||||
, ("hls-powered" , Vty.defAttr `Vty.withForeColor` Vty.green)
|
||||
, ("latest" , Vty.defAttr `Vty.withForeColor` Vty.yellow)
|
||||
, ("prerelease" , Vty.defAttr `Vty.withForeColor` Vty.red)
|
||||
, ("compiled" , Vty.defAttr `Vty.withForeColor` Vty.blue)
|
||||
, ("stray" , Vty.defAttr `Vty.withForeColor` Vty.blue)
|
||||
, ("help" , Vty.defAttr `Vty.withStyle` Vty.italic)
|
||||
]
|
||||
|
||||
@@ -173,19 +200,18 @@ withIOAction :: (AppState -> (Int, ListResult) -> IO (Either String a))
|
||||
withIOAction action as = case listSelectedElement (lr as) of
|
||||
Nothing -> continue as
|
||||
Just (ix, e) -> suspendAndResume $ do
|
||||
r <- action as (ix, e)
|
||||
case r of
|
||||
Left err -> throwIO $ userError err
|
||||
Right _ -> do
|
||||
apps <- (fmap . fmap)
|
||||
(\AppState {..} -> AppState { lr = listMoveTo ix lr, .. })
|
||||
$ getAppState Nothing (pfreq as)
|
||||
case apps of
|
||||
Right nas -> do
|
||||
putStrLn "Press enter to continue"
|
||||
_ <- getLine
|
||||
pure nas
|
||||
Left err -> throwIO $ userError err
|
||||
action as (ix, e) >>= \case
|
||||
Left err -> putStrLn $ ("Error: " <> err)
|
||||
Right _ -> putStrLn "Success"
|
||||
apps <- (fmap . fmap)
|
||||
(\AppState {..} -> AppState { lr = listMoveTo ix lr, .. })
|
||||
$ getAppState Nothing (pfreq as)
|
||||
case apps of
|
||||
Right nas -> do
|
||||
putStrLn "Press enter to continue"
|
||||
_ <- getLine
|
||||
pure nas
|
||||
Left err -> throwIO $ userError err
|
||||
|
||||
|
||||
install' :: AppState -> (Int, ListResult) -> IO (Either String ())
|
||||
@@ -213,13 +239,16 @@ install' AppState {..} (_, ListResult {..}) = do
|
||||
, TagNotFound
|
||||
, DigestError
|
||||
, DownloadFailed
|
||||
, NoUpdate]
|
||||
, NoUpdate
|
||||
, TarDirDoesNotExist
|
||||
]
|
||||
|
||||
(run $ do
|
||||
case lTool of
|
||||
GHC -> liftE $ installGHCBin dls lVer pfreq
|
||||
Cabal -> liftE $ installCabalBin dls lVer pfreq
|
||||
GHCup -> liftE $ upgradeGHCup dls Nothing False pfreq $> ()
|
||||
HLS -> liftE $ installHLSBin dls lVer pfreq $> ()
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> pure $ Right ()
|
||||
@@ -248,6 +277,7 @@ set' _ (_, ListResult {..}) = do
|
||||
case lTool of
|
||||
GHC -> liftE $ setGHC (GHCTargetVersion lCross lVer) SetGHCOnly $> ()
|
||||
Cabal -> liftE $ setCabal lVer $> ()
|
||||
HLS -> liftE $ setHLS lVer $> ()
|
||||
GHCup -> pure ()
|
||||
)
|
||||
>>= \case
|
||||
@@ -267,6 +297,7 @@ del' _ (_, ListResult {..}) = do
|
||||
case lTool of
|
||||
GHC -> liftE $ rmGHCVer (GHCTargetVersion lCross lVer) $> ()
|
||||
Cabal -> liftE $ rmCabalVer lVer $> ()
|
||||
HLS -> liftE $ rmHLSVer lVer $> ()
|
||||
GHCup -> pure ()
|
||||
)
|
||||
>>= \case
|
||||
@@ -296,14 +327,15 @@ uri' = unsafePerformIO (newIORef Nothing)
|
||||
|
||||
settings' :: IORef Settings
|
||||
{-# NOINLINE settings' #-}
|
||||
settings' = unsafePerformIO
|
||||
(newIORef Settings { cache = True
|
||||
settings' = unsafePerformIO $ do
|
||||
dirs <- getDirs
|
||||
newIORef Settings { cache = True
|
||||
, noVerify = False
|
||||
, keepDirs = Never
|
||||
, downloader = Curl
|
||||
, verbose = False
|
||||
, ..
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
logger' :: IORef LoggerConfig
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE DataKinds #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
@@ -38,7 +39,6 @@ import Control.Monad.Fail ( MonadFail )
|
||||
import Control.Monad.Logger
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
import Data.Aeson ( eitherDecode )
|
||||
import Data.Bifunctor
|
||||
import Data.Char
|
||||
import Data.Either
|
||||
@@ -69,7 +69,6 @@ import URI.ByteString
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.UTF8 as UTF8
|
||||
import qualified Data.ByteString.Lazy.UTF8 as BLU
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
@@ -117,15 +116,17 @@ prettyToolVer (ToolTag t) = show t
|
||||
|
||||
data InstallCommand = InstallGHC InstallOptions
|
||||
| InstallCabal InstallOptions
|
||||
| InstallHLS InstallOptions
|
||||
|
||||
data InstallOptions = InstallOptions
|
||||
{ instVer :: Maybe ToolVersion
|
||||
, instPlatform :: Maybe PlatformRequest
|
||||
, instBindist :: Maybe DownloadInfo
|
||||
, instBindist :: Maybe URI
|
||||
}
|
||||
|
||||
data SetCommand = SetGHC SetOptions
|
||||
| SetCabal SetOptions
|
||||
| SetHLS SetOptions
|
||||
|
||||
data SetOptions = SetOptions
|
||||
{ sToolVer :: Maybe ToolVersion
|
||||
@@ -139,6 +140,7 @@ data ListOptions = ListOptions
|
||||
|
||||
data RmCommand = RmGHC RmOptions
|
||||
| RmCabal Version
|
||||
| RmHLS Version
|
||||
|
||||
data RmOptions = RmOptions
|
||||
{ ghcVer :: GHCTargetVersion
|
||||
@@ -206,8 +208,8 @@ opts =
|
||||
( long "keep"
|
||||
<> metavar "<always|errors|never>"
|
||||
<> help
|
||||
"Keep build directories? (default: never)"
|
||||
<> value Never
|
||||
"Keep build directories? (default: errors)"
|
||||
<> value Errors
|
||||
<> hidden
|
||||
)
|
||||
<*> option
|
||||
@@ -395,20 +397,52 @@ installParser =
|
||||
)
|
||||
)
|
||||
)
|
||||
<> command
|
||||
"hls"
|
||||
( InstallHLS
|
||||
<$> (info
|
||||
(installOpts <**> helper)
|
||||
( progDesc "Install haskell-languge-server"
|
||||
<> footerDoc (Just $ text installHLSFooter)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<|> (Right <$> installOpts)
|
||||
where
|
||||
installHLSFooter :: String
|
||||
installHLSFooter = [s|Discussion:
|
||||
Installs haskell-language-server binaries and wrapper
|
||||
into "~/.ghcup/bin"
|
||||
|
||||
Examples:
|
||||
# install recommended GHC
|
||||
ghcup install hls|]
|
||||
|
||||
installGHCFooter :: String
|
||||
installGHCFooter = [s|Discussion:
|
||||
Installs the specified GHC version (or a recommended default one) into
|
||||
a self-contained "~/.ghcup/ghc/<ghcver>" directory
|
||||
and symlinks the ghc binaries to "~/.ghcup/bin/<binary>-<ghcver>".|]
|
||||
and symlinks the ghc binaries to "~/.ghcup/bin/<binary>-<ghcver>".
|
||||
|
||||
Examples:
|
||||
# install recommended GHC
|
||||
ghcup install ghc
|
||||
|
||||
# install latest GHC
|
||||
ghcup install ghc latest
|
||||
|
||||
# install GHC 8.10.2
|
||||
ghcup install ghc 8.10.2
|
||||
|
||||
# install GHC head fedora bindist
|
||||
ghcup install ghc -u https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27 head|]
|
||||
|
||||
|
||||
installOpts :: Parser InstallOptions
|
||||
installOpts =
|
||||
(\p u v -> InstallOptions v p u)
|
||||
(\p (u, v) -> InstallOptions v p u)
|
||||
<$> (optional
|
||||
(option
|
||||
(eitherReader platformParser)
|
||||
@@ -420,18 +454,19 @@ installOpts =
|
||||
)
|
||||
)
|
||||
)
|
||||
<*> (optional
|
||||
(option
|
||||
(eitherReader bindistParser)
|
||||
( short 'u'
|
||||
<> long "url"
|
||||
<> metavar "BINDIST_URL"
|
||||
<> help
|
||||
"Provide DownloadInfo as json string, e.g.: '{ \"dlHash\": \"<sha256 hash>\", \"dlSubdir\": \"ghc-<ver>\", \"dlUri\": \"<uri>\" }'"
|
||||
<*> ( ( (,)
|
||||
<$> (optional
|
||||
(option
|
||||
(eitherReader bindistParser)
|
||||
(short 'u' <> long "url" <> metavar "BINDIST_URL" <> help
|
||||
"Install the specified version from this bindist"
|
||||
)
|
||||
)
|
||||
)
|
||||
<*> (Just <$> toolVersionArgument)
|
||||
)
|
||||
)
|
||||
<|> ((,) <$> pure Nothing <*> optional toolVersionArgument)
|
||||
)
|
||||
<*> optional toolVersionArgument
|
||||
|
||||
|
||||
setParser :: Parser (Either SetCommand SetOptions)
|
||||
@@ -457,6 +492,16 @@ setParser =
|
||||
)
|
||||
)
|
||||
)
|
||||
<> command
|
||||
"hls"
|
||||
( SetHLS
|
||||
<$> (info
|
||||
(setOpts <**> helper)
|
||||
( progDesc "Set haskell-language-server version"
|
||||
<> footerDoc (Just $ text setHLSFooter)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<|> (Right <$> setOpts)
|
||||
@@ -471,6 +516,10 @@ setParser =
|
||||
setCabalFooter = [s|Discussion:
|
||||
Sets the the current Cabal version.|]
|
||||
|
||||
setHLSFooter :: String
|
||||
setHLSFooter = [s|Discussion:
|
||||
Sets the the current haskell-language-server version.|]
|
||||
|
||||
|
||||
setOpts :: Parser SetOptions
|
||||
setOpts = SetOptions <$> optional toolVersionArgument
|
||||
@@ -513,6 +562,13 @@ rmParser =
|
||||
(progDesc "Remove Cabal version")
|
||||
)
|
||||
)
|
||||
<> command
|
||||
"hls"
|
||||
( RmHLS
|
||||
<$> (info (versionParser' <**> helper)
|
||||
(progDesc "Remove haskell-language-server version")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
<|> (Right <$> rmOpts)
|
||||
@@ -814,18 +870,19 @@ platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
|
||||
pure v
|
||||
|
||||
|
||||
bindistParser :: String -> Either String DownloadInfo
|
||||
bindistParser = eitherDecode . BLU.fromString
|
||||
bindistParser :: String -> Either String URI
|
||||
bindistParser = first show . parseURI strictURIParserOptions . UTF8.fromString
|
||||
|
||||
|
||||
toSettings :: Options -> Settings
|
||||
toSettings Options {..} =
|
||||
toSettings :: Options -> IO Settings
|
||||
toSettings Options {..} = do
|
||||
let cache = optCache
|
||||
noVerify = optNoVerify
|
||||
keepDirs = optKeepDirs
|
||||
downloader = optsDownloader
|
||||
verbose = optVerbose
|
||||
in Settings { .. }
|
||||
dirs <- getDirs
|
||||
pure $ Settings { .. }
|
||||
|
||||
|
||||
upgradeOptsP :: Parser UpgradeOpts
|
||||
@@ -901,14 +958,13 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
(footerDoc (Just $ text main_footer))
|
||||
)
|
||||
>>= \opt@Options {..} -> do
|
||||
let settings@Settings{..} = toSettings opt
|
||||
settings@Settings{dirs = Dirs{..}, ..} <- toSettings opt
|
||||
|
||||
-- create ~/.ghcup dir
|
||||
ghcdir <- ghcupBaseDir
|
||||
createDirIfMissing newDirPerms ghcdir
|
||||
createDirRecursive' baseDir
|
||||
|
||||
-- logger interpreter
|
||||
logfile <- initGHCupFileLogging [rel|ghcup.log|]
|
||||
logfile <- flip runReaderT settings $ initGHCupFileLogging [rel|ghcup.log|]
|
||||
let loggerConfig = LoggerConfig
|
||||
{ lcPrintDebug = optVerbose
|
||||
, colorOutter = B.hPut stderr
|
||||
@@ -921,9 +977,9 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
-- Effect interpreters --
|
||||
-------------------------
|
||||
|
||||
let runInstTool =
|
||||
let runInstTool' settings' =
|
||||
runLogger
|
||||
. flip runReaderT settings
|
||||
. flip runReaderT settings'
|
||||
. runResourceT
|
||||
. runE
|
||||
@'[ AlreadyInstalled
|
||||
@@ -939,8 +995,11 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
, TagNotFound
|
||||
, DigestError
|
||||
, DownloadFailed
|
||||
, TarDirDoesNotExist
|
||||
]
|
||||
|
||||
let runInstTool = runInstTool' settings
|
||||
|
||||
let
|
||||
runSetGHC =
|
||||
runLogger
|
||||
@@ -954,12 +1013,22 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
let
|
||||
runSetCabal =
|
||||
runLogger
|
||||
. flip runReaderT settings
|
||||
. runE
|
||||
@'[ NotInstalled
|
||||
, TagNotFound
|
||||
]
|
||||
|
||||
let runListGHC = runLogger
|
||||
let
|
||||
runSetHLS =
|
||||
runLogger
|
||||
. flip runReaderT settings
|
||||
. runE
|
||||
@'[ NotInstalled
|
||||
, TagNotFound
|
||||
]
|
||||
|
||||
let runListGHC = runLogger . flip runReaderT settings
|
||||
|
||||
let runRm =
|
||||
runLogger . flip runReaderT settings . runE @'[NotInstalled]
|
||||
@@ -984,6 +1053,8 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
, NotFoundInPATH
|
||||
, PatchFailed
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
, NotInstalled
|
||||
#if !defined(TAR)
|
||||
, ArchiveResult
|
||||
#endif
|
||||
@@ -1003,6 +1074,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
, NotInstalled
|
||||
, PatchFailed
|
||||
, UnknownArchive
|
||||
, TarDirDoesNotExist
|
||||
#if !defined(TAR)
|
||||
, ArchiveResult
|
||||
#endif
|
||||
@@ -1052,7 +1124,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
|
||||
case optCommand of
|
||||
Upgrade _ _ -> pure ()
|
||||
_ -> runLogger $ checkForUpdates dls pfreq
|
||||
_ -> runLogger $ flip runReaderT settings $ checkForUpdates dls pfreq
|
||||
|
||||
|
||||
|
||||
@@ -1061,11 +1133,16 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
-----------------------
|
||||
|
||||
let installGHC InstallOptions{..} =
|
||||
(runInstTool $ do
|
||||
v <- liftE $ fromVersion dls instVer GHC
|
||||
case instBindist of
|
||||
Nothing -> liftE $ installGHCBin dls (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
Just uri -> liftE $ installGHCBindist uri (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool $ do
|
||||
v <- liftE $ fromVersion dls instVer GHC
|
||||
liftE $ installGHCBin dls (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
Just uri -> runInstTool' settings{noVerify = True} $ do
|
||||
v <- liftE $ fromVersion dls instVer GHC
|
||||
liftE $ installGHCBindist
|
||||
(DownloadInfo uri (Just $ RegexDir "ghc-.*") "")
|
||||
(_tvVersion v)
|
||||
(fromMaybe pfreq instPlatform)
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> do
|
||||
@@ -1073,13 +1150,13 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ $(logWarn)
|
||||
[i|GHC ver #{prettyVer v} already installed|]
|
||||
[i|GHC ver #{prettyVer v} already installed, you may want to run 'ghcup rm ghc #{prettyVer v}' first|]
|
||||
pure ExitSuccess
|
||||
VLeft (V (BuildFailed tmpdir e)) -> do
|
||||
case keepDirs of
|
||||
Never -> runLogger ($(logError) [i|Build failed with #{e}|])
|
||||
_ -> runLogger ($(logError) [i|Build failed with #{e}
|
||||
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||
Check the logs at #{logsDir} and the build directory #{tmpdir} for more clues.
|
||||
Make sure to clean up #{tmpdir} afterwards.|])
|
||||
pure $ ExitFailure 3
|
||||
VLeft (V NoDownload) -> do
|
||||
@@ -1092,16 +1169,21 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
$(logError) [i|#{e}|]
|
||||
$(logError) [i|Also check the logs in ~/.ghcup/logs|]
|
||||
$(logError) [i|Also check the logs in #{logsDir}|]
|
||||
pure $ ExitFailure 3
|
||||
|
||||
|
||||
let installCabal InstallOptions{..} =
|
||||
(runInstTool $ do
|
||||
v <- liftE $ fromVersion dls instVer Cabal
|
||||
case instBindist of
|
||||
Nothing -> liftE $ installCabalBin dls (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
Just uri -> liftE $ installCabalBindist uri (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool $ do
|
||||
v <- liftE $ fromVersion dls instVer Cabal
|
||||
liftE $ installCabalBin dls (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
Just uri -> runInstTool' settings{noVerify = True} $ do
|
||||
v <- liftE $ fromVersion dls instVer Cabal
|
||||
liftE $ installCabalBindist
|
||||
(DownloadInfo uri Nothing "")
|
||||
(_tvVersion v)
|
||||
(fromMaybe pfreq instPlatform)
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> do
|
||||
@@ -1109,7 +1191,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ $(logWarn)
|
||||
[i|Cabal ver #{prettyVer v} already installed|]
|
||||
[i|Cabal ver #{prettyVer v} already installed, you may want to run 'ghcup rm cabal #{prettyVer v}' first|]
|
||||
pure ExitSuccess
|
||||
VLeft (V NoDownload) -> do
|
||||
|
||||
@@ -1121,9 +1203,43 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
$(logError) [i|#{e}|]
|
||||
$(logError) [i|Also check the logs in ~/.ghcup/logs|]
|
||||
$(logError) [i|Also check the logs in #{logsDir}|]
|
||||
pure $ ExitFailure 4
|
||||
|
||||
let installHLS InstallOptions{..} =
|
||||
(case instBindist of
|
||||
Nothing -> runInstTool $ do
|
||||
v <- liftE $ fromVersion dls instVer HLS
|
||||
liftE $ installHLSBin dls (_tvVersion v) (fromMaybe pfreq instPlatform)
|
||||
Just uri -> runInstTool' settings{noVerify = True} $ do
|
||||
v <- liftE $ fromVersion dls instVer HLS
|
||||
liftE $ installHLSBindist
|
||||
(DownloadInfo uri Nothing "")
|
||||
(_tvVersion v)
|
||||
(fromMaybe pfreq instPlatform)
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> do
|
||||
runLogger $ $(logInfo) ("HLS installation successful")
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ $(logWarn)
|
||||
[i|HLS ver #{prettyVer v} already installed, you may want to run 'ghcup rm hls #{prettyVer v}' first|]
|
||||
pure ExitSuccess
|
||||
VLeft (V NoDownload) -> do
|
||||
|
||||
runLogger $ do
|
||||
case instVer of
|
||||
Just iver -> $(logError) [i|No available HLS version for #{prettyToolVer iver}|]
|
||||
Nothing -> $(logError) [i|No available recommended HLS version|]
|
||||
pure $ ExitFailure 4
|
||||
VLeft e -> do
|
||||
runLogger $ do
|
||||
$(logError) [i|#{e}|]
|
||||
$(logError) [i|Also check the logs in #{logsDir}|]
|
||||
pure $ ExitFailure 4
|
||||
|
||||
|
||||
let setGHC' SetOptions{..} =
|
||||
(runSetGHC $ do
|
||||
v <- liftE $ fromVersion dls sToolVer GHC
|
||||
@@ -1150,6 +1266,17 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
runLogger ($(logError) [i|#{e}|])
|
||||
pure $ ExitFailure 14
|
||||
|
||||
let setHLS' SetOptions{..} =
|
||||
(runSetHLS $ do
|
||||
v <- liftE $ fromVersion dls sToolVer HLS
|
||||
liftE $ setHLS (_tvVersion v)
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger ($(logError) [i|#{e}|])
|
||||
pure $ ExitFailure 14
|
||||
|
||||
let rmGHC' RmOptions{..} =
|
||||
(runRm $ do
|
||||
liftE $ rmGHCVer ghcVer
|
||||
@@ -1170,6 +1297,15 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
runLogger ($(logError) [i|#{e}|])
|
||||
pure $ ExitFailure 15
|
||||
|
||||
let rmHLS' tv =
|
||||
(runRm $ do
|
||||
liftE $ rmHLSVer tv
|
||||
)
|
||||
>>= \case
|
||||
VRight _ -> pure ExitSuccess
|
||||
VLeft e -> do
|
||||
runLogger ($(logError) [i|#{e}|])
|
||||
pure $ ExitFailure 15
|
||||
|
||||
|
||||
res <- case optCommand of
|
||||
@@ -1181,6 +1317,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
installGHC iopts
|
||||
Install (Left (InstallGHC iopts)) -> installGHC iopts
|
||||
Install (Left (InstallCabal iopts)) -> installCabal iopts
|
||||
Install (Left (InstallHLS iopts)) -> installHLS iopts
|
||||
InstallCabalLegacy iopts -> do
|
||||
runLogger ($(logWarn) [i|This is an old-style command for installing cabal. Use 'ghcup install cabal' instead.|])
|
||||
installCabal iopts
|
||||
@@ -1190,6 +1327,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
setGHC' sopts
|
||||
Set (Left (SetGHC sopts)) -> setGHC' sopts
|
||||
Set (Left (SetCabal sopts)) -> setCabal' sopts
|
||||
Set (Left (SetHLS sopts)) -> setHLS' sopts
|
||||
|
||||
List (ListOptions {..}) ->
|
||||
(runListGHC $ do
|
||||
@@ -1203,6 +1341,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
rmGHC' rmopts
|
||||
Rm (Left (RmGHC rmopts)) -> rmGHC' rmopts
|
||||
Rm (Left (RmCabal rmopts)) -> rmCabal' rmopts
|
||||
Rm (Left (RmHLS rmopts)) -> rmHLS' rmopts
|
||||
|
||||
DInfo ->
|
||||
do
|
||||
@@ -1232,14 +1371,14 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||
pure ExitSuccess
|
||||
VLeft (V (AlreadyInstalled _ v)) -> do
|
||||
runLogger $ $(logWarn)
|
||||
[i|GHC ver #{prettyVer v} already installed|]
|
||||
[i|GHC ver #{prettyVer v} already installed, you may want to run 'ghcup rm ghc #{prettyVer v}' first|]
|
||||
pure ExitSuccess
|
||||
VLeft (V (BuildFailed tmpdir e)) -> do
|
||||
case keepDirs of
|
||||
Never -> runLogger ($(logError) [i|Build failed with #{e}
|
||||
Check the logs at ~/.ghcup/logs|])
|
||||
Check the logs at #{logsDir}|])
|
||||
_ -> runLogger ($(logError) [i|Build failed with #{e}
|
||||
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||
Check the logs at #{logsDir} and the build directory #{tmpdir} for more clues.
|
||||
Make sure to clean up #{tmpdir} afterwards.|])
|
||||
pure $ ExitFailure 9
|
||||
VLeft e -> do
|
||||
@@ -1261,7 +1400,7 @@ Make sure to clean up #{tmpdir} afterwards.|])
|
||||
case keepDirs of
|
||||
Never -> runLogger ($(logError) [i|Build failed with #{e}|])
|
||||
_ -> runLogger ($(logError) [i|Build failed with #{e}
|
||||
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||
Check the logs at #{logsDir} and the build directory #{tmpdir} for more clues.
|
||||
Make sure to clean up #{tmpdir} afterwards.|])
|
||||
pure $ ExitFailure 10
|
||||
VLeft e -> do
|
||||
@@ -1275,9 +1414,7 @@ Make sure to clean up #{tmpdir} afterwards.|])
|
||||
p <- parseAbs . E.encodeUtf8 . T.pack $ efp
|
||||
pure $ Just p
|
||||
(UpgradeAt p) -> pure $ Just p
|
||||
UpgradeGHCupDir -> do
|
||||
bdir <- liftIO $ ghcupBinDir
|
||||
pure (Just (bdir </> [rel|ghcup|]))
|
||||
UpgradeGHCupDir -> pure (Just (binDir </> [rel|ghcup|]))
|
||||
|
||||
(runUpgrade $ (liftE $ upgradeGHCup dls target force pfreq)) >>= \case
|
||||
VRight v' -> do
|
||||
@@ -1412,7 +1549,8 @@ printListResult raw lr = do
|
||||
Just c -> T.unpack (c <> "-" <> prettyVer lVer)
|
||||
, intercalate "," $ (fmap printTag $ sort lTag)
|
||||
, intercalate ","
|
||||
$ (if fromSrc then [color' Blue "compiled"] else mempty)
|
||||
$ (if hlsPowered then [color' Green "hls-powered"] else mempty)
|
||||
++ (if fromSrc then [color' Blue "compiled"] else mempty)
|
||||
++ (if lStray then [color' Yellow "stray"] else mempty)
|
||||
++ (if lNoBindist then [color' Red "no-bindist"] else mempty)
|
||||
]
|
||||
@@ -1422,13 +1560,14 @@ printListResult raw lr = do
|
||||
where
|
||||
printTag Recommended = color' Green "recommended"
|
||||
printTag Latest = color' Yellow "latest"
|
||||
printTag Prerelease = color' Red "prerelease"
|
||||
printTag (Base pvp'') = "base-" ++ T.unpack (prettyPVP pvp'')
|
||||
printTag (UnknownTag t ) = t
|
||||
color' = case raw of
|
||||
True -> flip const
|
||||
False -> color
|
||||
|
||||
checkForUpdates :: (MonadCatch m, MonadLogger m, MonadThrow m, MonadIO m, MonadFail m, MonadLogger m)
|
||||
checkForUpdates :: (MonadReader Settings m, MonadCatch m, MonadLogger m, MonadThrow m, MonadIO m, MonadFail m, MonadLogger m)
|
||||
=> GHCupDownloads
|
||||
-> PlatformRequest
|
||||
-> m ()
|
||||
@@ -1453,6 +1592,13 @@ checkForUpdates dls pfreq = do
|
||||
$ $(logWarn)
|
||||
[i|New Cabal version available: #{prettyVer l}. To upgrade, run 'ghcup install cabal #{prettyVer l}'|]
|
||||
|
||||
forM_ (getLatest dls HLS) $ \l -> do
|
||||
mcabal_ver <- latestInstalled HLS
|
||||
forM mcabal_ver $ \cabal_ver ->
|
||||
when (l > cabal_ver)
|
||||
$ $(logWarn)
|
||||
[i|New HLS version available: #{prettyVer l}. To upgrade, run 'ghcup install hls #{prettyVer l}'|]
|
||||
|
||||
where
|
||||
latestInstalled tool = (fmap lVer . lastMay)
|
||||
<$> (listVersions dls (Just tool) (Just ListInstalled) pfreq)
|
||||
@@ -1468,20 +1614,4 @@ GHCup cache directory: #{toFilePath diCacheDir}
|
||||
Architecture: #{prettyArch diArch}
|
||||
Platform: #{prettyPlatform diPlatform}
|
||||
Version: #{describe_result}|]
|
||||
where
|
||||
prettyArch :: Architecture -> String
|
||||
prettyArch A_64 = "amd64"
|
||||
prettyArch A_32 = "i386"
|
||||
prettyArch A_PowerPC = "PowerPC"
|
||||
prettyArch A_PowerPC64 = "PowerPC64"
|
||||
prettyArch A_Sparc = "Sparc"
|
||||
prettyArch A_Sparc64 = "Sparc64"
|
||||
prettyArch A_ARM = "ARM"
|
||||
prettyArch A_ARM64 = "ARM64"
|
||||
|
||||
prettyPlatform :: PlatformResult -> String
|
||||
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Just v' }
|
||||
= show plat <> ", " <> show v'
|
||||
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Nothing }
|
||||
= show plat
|
||||
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
(
|
||||
|
||||
: "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}"
|
||||
|
||||
export GHCUP_USE_XDG_DIRS
|
||||
|
||||
if [ -n "${GHCUP_USE_XDG_DIRS}" ] ; then
|
||||
GHCUP_DIR=${XDG_DATA_HOME:=$HOME/.local}/ghcup
|
||||
GHCUP_BIN=${XDG_BIN_HOME:=$HOME/.local/bin}
|
||||
else
|
||||
GHCUP_DIR=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup
|
||||
GHCUP_BIN=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin
|
||||
fi
|
||||
|
||||
: "${BOOTSTRAP_HASKELL_GHC_VERSION:=recommended}"
|
||||
: "${BOOTSTRAP_HASKELL_CABAL_VERSION:=recommended}"
|
||||
|
||||
@@ -29,10 +40,26 @@ _eghcup() {
|
||||
fi
|
||||
}
|
||||
|
||||
_done() {
|
||||
echo
|
||||
echo "All done!"
|
||||
echo
|
||||
echo "To start a simple repl, run:"
|
||||
echo " ghci"
|
||||
echo
|
||||
echo "To start a new haskell project in the current directory, run:"
|
||||
echo " cabal init --interactive"
|
||||
echo
|
||||
echo "To install other GHC versions, run:"
|
||||
echo " ghcup tui"
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
download_ghcup() {
|
||||
_plat="$(uname -s)"
|
||||
_arch=$(uname -m)
|
||||
_ghver="0.1.8"
|
||||
_ghver="0.1.10"
|
||||
_base_url="https://downloads.haskell.org/~ghcup"
|
||||
|
||||
case "${_plat}" in
|
||||
@@ -83,15 +110,15 @@ download_ghcup() {
|
||||
;;
|
||||
esac
|
||||
|
||||
edo curl -Lf "${_url}" > "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin/ghcup
|
||||
edo curl -Lf "${_url}" > "${GHCUP_BIN}"/ghcup
|
||||
|
||||
edo chmod +x "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin/ghcup
|
||||
edo chmod +x "${GHCUP_BIN}"/ghcup
|
||||
|
||||
cat <<-EOF > "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/env || die "Failed to create env file"
|
||||
export PATH="\$HOME/.cabal/bin:\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/bin:\$PATH"
|
||||
cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file"
|
||||
export PATH="\$HOME/.cabal/bin:${GHCUP_BIN}:\$PATH"
|
||||
EOF
|
||||
# shellcheck disable=SC1090
|
||||
edo . "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/env
|
||||
edo . "${GHCUP_DIR}"/env
|
||||
eghcup upgrade
|
||||
|
||||
unset _plat _arch _url _ghver _base_url
|
||||
@@ -102,12 +129,19 @@ echo
|
||||
echo "Welcome to Haskell!"
|
||||
echo
|
||||
echo "This script will download and install the following binaries:"
|
||||
echo " * ghcup - The Haskell toolchain installer (for managing GHC/cabal versions)"
|
||||
echo " * ghcup - The Haskell toolchain installer"
|
||||
echo " (for managing GHC/cabal versions)"
|
||||
echo " * ghc - The Glasgow Haskell Compiler"
|
||||
echo " * cabal - The Cabal build tool"
|
||||
echo
|
||||
echo "ghcup installs only into the following directory, which can be removed anytime:"
|
||||
echo " $GHCUP_INSTALL_BASE_PREFIX/.ghcup"
|
||||
if [ -z "${GHCUP_USE_XDG_DIRS}" ] ; then
|
||||
echo "ghcup installs only into the following directory,"
|
||||
echo "which can be removed anytime:"
|
||||
echo " $GHCUP_INSTALL_BASE_PREFIX/.ghcup"
|
||||
else
|
||||
echo "ghcup installs into XDG directories as long as"
|
||||
echo "'GHCUP_USE_XDG_DIRS' is set."
|
||||
fi
|
||||
echo
|
||||
|
||||
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
@@ -119,7 +153,7 @@ if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
read -r answer </dev/tty
|
||||
fi
|
||||
|
||||
edo mkdir -p "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin
|
||||
edo mkdir -p "${GHCUP_BIN}"
|
||||
|
||||
if command -V "ghcup" >/dev/null 2>&1 ; then
|
||||
if [ -z "${BOOTSTRAP_HASKELL_NO_UPGRADE}" ] ; then
|
||||
@@ -156,7 +190,7 @@ printf "\\033[0;35m%s\\033[0m\\n" ""
|
||||
|
||||
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
echo "In order to run ghc and cabal, you need to adjust your PATH variable."
|
||||
echo "You may want to source '$GHCUP_INSTALL_BASE_PREFIX/.ghcup/env' in your shell"
|
||||
echo "You may want to source '$GHCUP_DIR/env' in your shell"
|
||||
echo "configuration to do so (e.g. ~/.bashrc)."
|
||||
|
||||
case $SHELL in
|
||||
@@ -174,13 +208,13 @@ if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
GHCUP_PROFILE_FILE="$HOME/.zshrc"
|
||||
MY_SHELL="zsh"
|
||||
else
|
||||
exit 0
|
||||
_done
|
||||
fi
|
||||
;;
|
||||
*/fish) # login shell is fish
|
||||
GHCUP_PROFILE_FILE="$HOME/.config/fish/config.fish"
|
||||
MY_SHELL="fish" ;;
|
||||
*) exit 0 ;;
|
||||
*) _done ;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -198,18 +232,24 @@ if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||
case $MY_SHELL in
|
||||
"") break ;;
|
||||
fish)
|
||||
echo "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME" >> "${GHCUP_PROFILE_FILE}"
|
||||
echo "test -f \$GHCUP_INSTALL_BASE_PREFIX/.ghcup/env ; and set -gx PATH \$HOME/.cabal/bin \$GHCUP_INSTALL_BASE_PREFIX/.ghcup/bin \$PATH" >> "${GHCUP_PROFILE_FILE}"
|
||||
if ! grep -q "ghcup-env" "${GHCUP_PROFILE_FILE}" ; then
|
||||
echo "# ghcup-env" >> "${GHCUP_PROFILE_FILE}"
|
||||
echo "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME" >> "${GHCUP_PROFILE_FILE}"
|
||||
echo "test -f $GHCUP_DIR/env ; and set -gx PATH \$HOME/.cabal/bin $GHCUP_BIN \$PATH" >> "${GHCUP_PROFILE_FILE}"
|
||||
fi
|
||||
break ;;
|
||||
*)
|
||||
echo "[ -f \"\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/env\" ] && source \"\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/env\"" >> "${GHCUP_PROFILE_FILE}"
|
||||
if ! grep -q "ghcup-env" "${GHCUP_PROFILE_FILE}" ; then
|
||||
echo "[ -f \"${GHCUP_DIR}/env\" ] && source \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}"
|
||||
fi
|
||||
break ;;
|
||||
esac
|
||||
printf "\\033[0;35m%s\\033[0m\\n" "OK! ${GHCUP_PROFILE_FILE} has been modified. Restart your terminal for the changes to take effect,"
|
||||
printf "\\033[0;35m%s\\033[0m\\n" "or type \"source ${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/env\" to apply them in your current terminal session."
|
||||
exit 0;;
|
||||
printf "\\033[0;35m%s\\033[0m\\n" "or type \"source ${GHCUP_DIR}/env\" to apply them in your current terminal session."
|
||||
_done
|
||||
;;
|
||||
[Nn]*)
|
||||
exit 0;;
|
||||
_done ;;
|
||||
*)
|
||||
echo "Please type YES or NO and press enter.";;
|
||||
esac
|
||||
|
||||
@@ -8,6 +8,24 @@ source-repository-package
|
||||
tag: 80a1c5fc07f7226c424250ec17f674cd4d618f42
|
||||
subdir: haskus-utils-types
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/hasufell/hpath.git
|
||||
tag: bf6d28cf989b70286e12fecc183d5bbf5454a1a2
|
||||
subdir: hpath-io
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/hasufell/hpath.git
|
||||
tag: bf6d28cf989b70286e12fecc183d5bbf5454a1a2
|
||||
subdir: hpath-directory
|
||||
|
||||
-- https://github.com/cjdev/text-conversions/pull/10
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/hasufell/text-conversions.git
|
||||
tag: 9abf0e5e5664a3178367597c32db19880477a53c
|
||||
|
||||
optimization: 2
|
||||
|
||||
package streamly
|
||||
@@ -19,6 +37,6 @@ package ghcup
|
||||
constraints: http-io-streams -brotli
|
||||
|
||||
package libarchive
|
||||
flags: static
|
||||
flags: -system-libarchive
|
||||
|
||||
allow-newer: base ghc-prim template-haskell
|
||||
allow-newer: base, ghc-prim, template-haskell
|
||||
|
||||
2558
ghcup-0.0.2.json
2558
ghcup-0.0.2.json
File diff suppressed because it is too large
Load Diff
1444
ghcup-0.0.2.yaml
Normal file
1444
ghcup-0.0.2.yaml
Normal file
File diff suppressed because it is too large
Load Diff
68
ghcup.cabal
68
ghcup.cabal
@@ -1,6 +1,6 @@
|
||||
cabal-version: 3.0
|
||||
name: ghcup
|
||||
version: 0.1.8
|
||||
version: 0.1.11
|
||||
synopsis: ghc toolchain installer as an exe/library
|
||||
description:
|
||||
A rewrite of the shell script ghcup, for providing
|
||||
@@ -81,6 +81,9 @@ common containers
|
||||
common cryptohash-sha256
|
||||
build-depends: cryptohash-sha256 >= 0.11.101.0
|
||||
|
||||
common generic-arbitrary
|
||||
build-depends: generic-arbitrary >=0.1.0
|
||||
|
||||
common generics-sop
|
||||
build-depends: generics-sop >=0.5
|
||||
|
||||
@@ -94,13 +97,13 @@ common hpath
|
||||
build-depends: hpath >=0.11
|
||||
|
||||
common hpath-directory
|
||||
build-depends: hpath-directory >=0.14
|
||||
build-depends: hpath-directory >=0.14.1
|
||||
|
||||
common hpath-filepath
|
||||
build-depends: hpath-filepath >=0.10.3
|
||||
|
||||
common hpath-io
|
||||
build-depends: hpath-io >=0.14
|
||||
build-depends: hpath-io >=0.14.1
|
||||
|
||||
common hpath-posix
|
||||
build-depends: hpath-posix >=0.13.2
|
||||
@@ -108,11 +111,17 @@ common hpath-posix
|
||||
common http-io-streams
|
||||
build-depends: http-io-streams >=0.1.2.0
|
||||
|
||||
common hspec
|
||||
build-depends: hspec >=2.7.4
|
||||
|
||||
common hspec-golden-aeson
|
||||
build-depends: hspec-golden-aeson >=0.7
|
||||
|
||||
common io-streams
|
||||
build-depends: io-streams >=1.5
|
||||
|
||||
common libarchive
|
||||
build-depends: libarchive >= 2.2.5.2
|
||||
build-depends: libarchive >= 3.0.0.0
|
||||
|
||||
common lzma
|
||||
build-depends: lzma >=0.0.0.3
|
||||
@@ -153,6 +162,9 @@ common safe
|
||||
common safe-exceptions
|
||||
build-depends: safe-exceptions >=0.1
|
||||
|
||||
common split
|
||||
build-depends: split >=0.2.3.4
|
||||
|
||||
common streamly
|
||||
build-depends: streamly >=0.7.1
|
||||
|
||||
@@ -192,6 +204,12 @@ common transformers
|
||||
common os-release
|
||||
build-depends: os-release >=1.0.0
|
||||
|
||||
common QuickCheck
|
||||
build-depends: QuickCheck >=2.14.1
|
||||
|
||||
common quickcheck-arbitrary-adt
|
||||
build-depends: quickcheck-arbitrary-adt >=0.3.1.0
|
||||
|
||||
common unix
|
||||
build-depends: unix >=2.7
|
||||
|
||||
@@ -219,6 +237,9 @@ common vty
|
||||
common word8
|
||||
build-depends: word8 >=0.1.3
|
||||
|
||||
common yaml
|
||||
build-depends: yaml >=0.11.4.0
|
||||
|
||||
common zlib
|
||||
build-depends: zlib >=0.6.2.1
|
||||
|
||||
@@ -234,8 +255,6 @@ common config
|
||||
PackageImports
|
||||
RecordWildCards
|
||||
ScopedTypeVariables
|
||||
Strict
|
||||
StrictData
|
||||
TupleSections
|
||||
|
||||
library
|
||||
@@ -273,6 +292,7 @@ library
|
||||
, resourcet
|
||||
, safe
|
||||
, safe-exceptions
|
||||
, split
|
||||
, streamly
|
||||
, streamly-posix
|
||||
, streamly-bytestring
|
||||
@@ -291,13 +311,11 @@ library
|
||||
, vector
|
||||
, versions
|
||||
, word8
|
||||
, yaml
|
||||
, zlib
|
||||
|
||||
exposed-modules:
|
||||
GHCup
|
||||
GHCup.Data.GHCupDownloads
|
||||
GHCup.Data.GHCupInfo
|
||||
GHCup.Data.ToolRequirements
|
||||
GHCup.Download
|
||||
GHCup.Download.Utils
|
||||
GHCup.Errors
|
||||
@@ -316,6 +334,10 @@ library
|
||||
GHCup.Utils.Version.QQ
|
||||
GHCup.Version
|
||||
|
||||
default-extensions:
|
||||
Strict
|
||||
StrictData
|
||||
|
||||
-- other-modules:
|
||||
-- other-extensions:
|
||||
hs-source-dirs: lib
|
||||
@@ -372,6 +394,10 @@ executable ghcup
|
||||
hs-source-dirs: app/ghcup
|
||||
default-language: Haskell2010
|
||||
|
||||
default-extensions:
|
||||
Strict
|
||||
StrictData
|
||||
|
||||
if flag(internal-downloader)
|
||||
cpp-options: -DINTERNAL_DOWNLOADER
|
||||
|
||||
@@ -413,6 +439,7 @@ executable ghcup-gen
|
||||
, uri-bytestring
|
||||
, utf8-string
|
||||
, versions
|
||||
, yaml
|
||||
|
||||
--
|
||||
main-is: Main.hs
|
||||
@@ -425,8 +452,25 @@ executable ghcup-gen
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite ghcup-test
|
||||
default-language: Haskell2010
|
||||
import:
|
||||
config
|
||||
, base
|
||||
, bytestring
|
||||
, containers
|
||||
, QuickCheck
|
||||
, generic-arbitrary
|
||||
, hpath
|
||||
, hspec
|
||||
, hspec-golden-aeson
|
||||
, quickcheck-arbitrary-adt
|
||||
, text
|
||||
, uri-bytestring
|
||||
, versions
|
||||
type: exitcode-stdio-1.0
|
||||
build-depends: ghcup
|
||||
hs-source-dirs: test
|
||||
main-is: MyLibTest.hs
|
||||
build-depends: base >=4.12.0.0
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
GHCup.ArbitraryTypes
|
||||
GHCup.Types.JSONSpec
|
||||
Spec
|
||||
|
||||
10767
golden/GHCupInfo.json
Normal file
10767
golden/GHCupInfo.json
Normal file
File diff suppressed because it is too large
Load Diff
2
hie.yaml
2
hie.yaml
@@ -2,3 +2,5 @@ cradle:
|
||||
cabal:
|
||||
- path: "."
|
||||
component: "ghcup:lib:ghcup"
|
||||
- path: "."
|
||||
component: "ghcup:exe:ghcup"
|
||||
|
||||
772
lib/GHCup.hs
772
lib/GHCup.hs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
{-|
|
||||
Module : GHCup.Data.GHCupInfo
|
||||
Description :
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
-}
|
||||
module GHCup.Data.GHCupInfo where
|
||||
|
||||
import GHCup.Data.GHCupDownloads
|
||||
import GHCup.Data.ToolRequirements
|
||||
import GHCup.Types
|
||||
|
||||
|
||||
ghcupInfo :: GHCupInfo
|
||||
ghcupInfo = GHCupInfo { _toolRequirements = toolRequirements
|
||||
, _ghcupDownloads = ghcupDownloads
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Data.ToolRequirements
|
||||
Description : Tool requirements
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
-}
|
||||
module GHCup.Data.ToolRequirements where
|
||||
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.String.QQ
|
||||
import GHCup.Utils.Version.QQ
|
||||
|
||||
import qualified Data.Map as M
|
||||
|
||||
|
||||
|
||||
-- | Currently 'GHC' is used for both GHC and cabal to simplify
|
||||
-- this, until we need actual separation.
|
||||
toolRequirements :: ToolRequirements
|
||||
toolRequirements = M.fromList
|
||||
[ ( GHC
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, M.fromList
|
||||
[ ( Linux UnknownLinux
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[]
|
||||
[s|You need the following packages: curl g++ gcc gmp make ncurses realpath xz-utils. Consult your distro documentation on the exact names of those packages.|]
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( Linux Alpine
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[ "curl"
|
||||
, "gcc"
|
||||
, "g++"
|
||||
, "gmp-dev"
|
||||
, "ncurses-dev"
|
||||
, "libffi-dev"
|
||||
, "make"
|
||||
, "xz"
|
||||
, "tar"
|
||||
, "perl"
|
||||
]
|
||||
""
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( Linux Ubuntu
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[ "build-essential"
|
||||
, "curl"
|
||||
, "libffi-dev"
|
||||
, "libffi6"
|
||||
, "libgmp-dev"
|
||||
, "libgmp10"
|
||||
, "libncurses-dev"
|
||||
, "libncurses5"
|
||||
, "libtinfo5"
|
||||
]
|
||||
""
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( Linux Debian
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[ "build-essential"
|
||||
, "curl"
|
||||
, "libffi-dev"
|
||||
, "libffi6"
|
||||
, "libgmp-dev"
|
||||
, "libgmp10"
|
||||
, "libncurses-dev"
|
||||
, "libncurses5"
|
||||
, "libtinfo5"
|
||||
]
|
||||
""
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( Linux CentOS
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[ "gcc"
|
||||
, "gcc-c++"
|
||||
, "gmp"
|
||||
, "gmp-devel"
|
||||
, "make"
|
||||
, "ncurses"
|
||||
, "ncurses-compat-libs"
|
||||
, "xz"
|
||||
, "perl"
|
||||
]
|
||||
""
|
||||
),
|
||||
( Just [vers|7|]
|
||||
, Requirements
|
||||
[ "gcc"
|
||||
, "gcc-c++"
|
||||
, "gmp"
|
||||
, "gmp-devel"
|
||||
, "make"
|
||||
, "ncurses"
|
||||
, "xz"
|
||||
, "perl"
|
||||
]
|
||||
""
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( Darwin
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[]
|
||||
"On OS X, in the course of running ghcup you will be given a dialog box to install the command line tools. Accept and the requirements will be installed for you. You will then need to run the command again."
|
||||
)
|
||||
]
|
||||
)
|
||||
, ( FreeBSD
|
||||
, M.fromList
|
||||
[ ( Nothing
|
||||
, Requirements
|
||||
[ "curl"
|
||||
, "gcc"
|
||||
, "gmp"
|
||||
, "gmake"
|
||||
, "ncurses"
|
||||
, "perl5"
|
||||
, "libffi"
|
||||
, "libiconv"
|
||||
]
|
||||
""
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -13,7 +13,7 @@
|
||||
Module : GHCup.Download
|
||||
Description : Downloading
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -52,6 +52,7 @@ import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
hiding ( throwM )
|
||||
import Data.Aeson
|
||||
import Data.Bifunctor
|
||||
import Data.ByteString ( ByteString )
|
||||
#if defined(INTERNAL_DOWNLOADER)
|
||||
import Data.CaseInsensitive ( CI )
|
||||
@@ -88,6 +89,7 @@ import qualified Data.Map.Strict as M
|
||||
import qualified Data.Text as T
|
||||
#endif
|
||||
import qualified Data.Text.Encoding as E
|
||||
import qualified Data.Yaml as Y
|
||||
import qualified System.Posix.Files.ByteString as PF
|
||||
import qualified System.Posix.RawFilePath.Directory
|
||||
as RD
|
||||
@@ -103,7 +105,7 @@ import qualified System.Posix.RawFilePath.Directory
|
||||
|
||||
|
||||
-- | Like 'getDownloads', but tries to fall back to
|
||||
-- cached ~/.ghcup/cache/ghcup-<format-ver>.json
|
||||
-- cached ~/.ghcup/cache/ghcup-<format-ver>.yaml
|
||||
getDownloadsF :: ( FromJSONKey Tool
|
||||
, FromJSONKey Version
|
||||
, FromJSON VersionInfo
|
||||
@@ -131,17 +133,17 @@ getDownloadsF urlSource = do
|
||||
(OwnSpec _) -> liftE $ getDownloads urlSource
|
||||
where
|
||||
readFromCache = do
|
||||
Settings {dirs = Dirs {..}} <- lift ask
|
||||
lift $ $(logWarn)
|
||||
[i|Could not get download info, trying cached version (this may not be recent!)|]
|
||||
let path = view pathL' ghcupURL
|
||||
cacheDir <- liftIO $ ghcupCacheDir
|
||||
json_file <- (cacheDir </>) <$> urlBaseName path
|
||||
yaml_file <- (cacheDir </>) <$> urlBaseName path
|
||||
bs <-
|
||||
handleIO' NoSuchThing
|
||||
(\_ -> throwE $ FileDoesNotExistError (toFilePath json_file))
|
||||
(\_ -> throwE $ FileDoesNotExistError (toFilePath yaml_file))
|
||||
$ liftIO
|
||||
$ readFile json_file
|
||||
lE' JSONDecodeError $ eitherDecode' bs
|
||||
$ readFile yaml_file
|
||||
lE' JSONDecodeError $ bimap show id $ Y.decodeEither' (L.toStrict bs)
|
||||
|
||||
|
||||
-- | Downloads the download information! But only if we need to ;P
|
||||
@@ -162,10 +164,10 @@ getDownloads urlSource = do
|
||||
case urlSource of
|
||||
GHCupURL -> do
|
||||
bs <- reThrowAll DownloadFailed $ smartDl ghcupURL
|
||||
lE' JSONDecodeError $ eitherDecode' bs
|
||||
lE' JSONDecodeError $ bimap show id $ Y.decodeEither' (L.toStrict bs)
|
||||
(OwnSource url) -> do
|
||||
bs <- reThrowAll DownloadFailed $ downloadBS url
|
||||
lE' JSONDecodeError $ eitherDecode' bs
|
||||
lE' JSONDecodeError $ bimap show id $ Y.decodeEither' (L.toStrict bs)
|
||||
(OwnSpec av) -> pure $ av
|
||||
|
||||
where
|
||||
@@ -198,8 +200,8 @@ getDownloads urlSource = do
|
||||
m1
|
||||
L.ByteString
|
||||
smartDl uri' = do
|
||||
Settings {dirs = Dirs {..}} <- lift ask
|
||||
let path = view pathL' uri'
|
||||
cacheDir <- liftIO $ ghcupCacheDir
|
||||
json_file <- (cacheDir </>) <$> urlBaseName path
|
||||
e <- liftIO $ doesFileExist json_file
|
||||
if e
|
||||
@@ -224,7 +226,7 @@ getDownloads urlSource = do
|
||||
else -- access in less than 5 minutes, re-use file
|
||||
liftIO $ readFile json_file
|
||||
else do
|
||||
liftIO $ createDirIfMissing newDirPerms cacheDir
|
||||
liftIO $ createDirRecursive' cacheDir
|
||||
getModTime >>= \case
|
||||
Just modTime -> dlWithMod modTime json_file
|
||||
Nothing -> do
|
||||
@@ -328,7 +330,7 @@ download dli dest mfn
|
||||
scheme = view (dlUri % uriSchemeL' % schemeBSL') dli
|
||||
cp = do
|
||||
-- destination dir must exist
|
||||
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
||||
liftIO $ createDirRecursive' dest
|
||||
destFile <- getDestFile
|
||||
fromFile <- parseAbs path
|
||||
liftIO $ copyFile fromFile destFile Strict
|
||||
@@ -338,7 +340,7 @@ download dli dest mfn
|
||||
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
||||
|
||||
-- destination dir must exist
|
||||
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
||||
liftIO $ createDirRecursive' dest
|
||||
destFile <- getDestFile
|
||||
|
||||
-- download
|
||||
@@ -390,15 +392,15 @@ downloadCached dli mfn = do
|
||||
cache <- lift getCache
|
||||
case cache of
|
||||
True -> do
|
||||
cachedir <- liftIO $ ghcupCacheDir
|
||||
Settings {dirs = Dirs {..}} <- lift ask
|
||||
fn <- maybe (urlBaseName $ view (dlUri % pathL') dli) pure mfn
|
||||
let cachfile = cachedir </> fn
|
||||
let cachfile = cacheDir </> fn
|
||||
fileExists <- liftIO $ doesFileExist cachfile
|
||||
if
|
||||
| fileExists -> do
|
||||
liftE $ checkDigest dli cachfile
|
||||
pure $ cachfile
|
||||
| otherwise -> liftE $ download dli cachedir mfn
|
||||
| otherwise -> liftE $ download dli cacheDir mfn
|
||||
False -> do
|
||||
tmp <- lift withGHCupTmpDir
|
||||
liftE $ download dli tmp mfn
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Module : GHCup.Errors
|
||||
Description : GHCup error types
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -89,6 +89,9 @@ data JSONError = JSONDecodeError String
|
||||
data FileDoesNotExistError = FileDoesNotExistError ByteString
|
||||
deriving Show
|
||||
|
||||
data TarDirDoesNotExist = TarDirDoesNotExist TarDir
|
||||
deriving Show
|
||||
|
||||
-- | File digest verification failed.
|
||||
data DigestError = DigestError Text Text
|
||||
deriving Show
|
||||
@@ -149,3 +152,10 @@ data ParseError = ParseError String
|
||||
deriving Show
|
||||
|
||||
instance Exception ParseError
|
||||
|
||||
|
||||
data UnexpectedListLength = UnexpectedListLength String
|
||||
deriving Show
|
||||
|
||||
instance Exception UnexpectedListLength
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
Module : GHCup.Plaform
|
||||
Description : Retrieving platform information
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Module : GHCup.Requirements
|
||||
Description : Requirements utilities
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -48,7 +48,7 @@ prettyRequirements :: Requirements -> T.Text
|
||||
prettyRequirements Requirements {..} =
|
||||
let d = if not . null $ _distroPKGs
|
||||
then
|
||||
"\n Install the following distro packages: "
|
||||
"\n Please install the following distro packages: "
|
||||
<> T.intercalate " " _distroPKGs
|
||||
else ""
|
||||
n = if not . T.null $ _notes then "\n Note: " <> _notes else ""
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
Module : GHCup.Types
|
||||
Description : GHCup types
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -19,6 +19,7 @@ import Data.Versions
|
||||
import HPath
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.Text as T
|
||||
import qualified GHC.Generics as GHC
|
||||
|
||||
|
||||
@@ -75,6 +76,7 @@ type PlatformVersionSpec = Map (Maybe Versioning) DownloadInfo
|
||||
data Tool = GHC
|
||||
| Cabal
|
||||
| GHCup
|
||||
| HLS
|
||||
deriving (Eq, GHC.Generic, Ord, Show)
|
||||
|
||||
|
||||
@@ -86,15 +88,16 @@ data VersionInfo = VersionInfo
|
||||
, _viSourceDL :: Maybe DownloadInfo -- ^ source tarball
|
||||
, _viArch :: ArchitectureSpec -- ^ descend for binary downloads per arch
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
deriving (Eq, GHC.Generic, Show)
|
||||
|
||||
|
||||
-- | A tag. These are currently attached to a version of a tool.
|
||||
data Tag = Latest
|
||||
| Recommended
|
||||
| Prerelease
|
||||
| Base PVP
|
||||
| UnknownTag String -- ^ used for upwardscompat
|
||||
deriving (Ord, Eq, Show) -- FIXME: manual JSON instance
|
||||
deriving (Ord, Eq, GHC.Generic, Show) -- FIXME: manual JSON instance
|
||||
|
||||
|
||||
data Architecture = A_64
|
||||
@@ -107,6 +110,15 @@ data Architecture = A_64
|
||||
| A_ARM64
|
||||
deriving (Eq, GHC.Generic, Ord, Show)
|
||||
|
||||
prettyArch :: Architecture -> String
|
||||
prettyArch A_64 = "x86_64"
|
||||
prettyArch A_32 = "i386"
|
||||
prettyArch A_PowerPC = "powerpc"
|
||||
prettyArch A_PowerPC64 = "powerpc64"
|
||||
prettyArch A_Sparc = "sparc"
|
||||
prettyArch A_Sparc64 = "sparc64"
|
||||
prettyArch A_ARM = "arm"
|
||||
prettyArch A_ARM64 = "aarch64"
|
||||
|
||||
data Platform = Linux LinuxDistro
|
||||
-- ^ must exit
|
||||
@@ -115,6 +127,11 @@ data Platform = Linux LinuxDistro
|
||||
| FreeBSD
|
||||
deriving (Eq, GHC.Generic, Ord, Show)
|
||||
|
||||
prettyPlatfrom :: Platform -> String
|
||||
prettyPlatfrom (Linux distro) = "linux-" ++ prettyDistro distro
|
||||
prettyPlatfrom Darwin = "darwin"
|
||||
prettyPlatfrom FreeBSD = "freebsd"
|
||||
|
||||
data LinuxDistro = Debian
|
||||
| Ubuntu
|
||||
| Mint
|
||||
@@ -131,15 +148,28 @@ data LinuxDistro = Debian
|
||||
-- ^ must exit
|
||||
deriving (Eq, GHC.Generic, Ord, Show)
|
||||
|
||||
prettyDistro :: LinuxDistro -> String
|
||||
prettyDistro Debian = "debian"
|
||||
prettyDistro Ubuntu = "ubuntu"
|
||||
prettyDistro Mint= "mint"
|
||||
prettyDistro Fedora = "fedora"
|
||||
prettyDistro CentOS = "centos"
|
||||
prettyDistro RedHat = "redhat"
|
||||
prettyDistro Alpine = "alpine"
|
||||
prettyDistro AmazonLinux = "amazon"
|
||||
prettyDistro Gentoo = "gentoo"
|
||||
prettyDistro Exherbo = "exherbo"
|
||||
prettyDistro UnknownLinux = "unknown"
|
||||
|
||||
|
||||
-- | An encapsulation of a download. This can be used
|
||||
-- to download, extract and install a tool.
|
||||
data DownloadInfo = DownloadInfo
|
||||
{ _dlUri :: URI
|
||||
, _dlSubdir :: Maybe (Path Rel)
|
||||
, _dlSubdir :: Maybe TarDir
|
||||
, _dlHash :: Text
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
deriving (Eq, GHC.Generic, Show)
|
||||
|
||||
|
||||
|
||||
@@ -149,22 +179,39 @@ data DownloadInfo = DownloadInfo
|
||||
--------------
|
||||
|
||||
|
||||
-- | How to descend into a tar archive.
|
||||
data TarDir = RealDir (Path Rel)
|
||||
| RegexDir String -- ^ will be compiled to regex, the first match will "win"
|
||||
deriving (Eq, GHC.Generic, Show)
|
||||
|
||||
|
||||
-- | Where to fetch GHCupDownloads from.
|
||||
data URLSource = GHCupURL
|
||||
| OwnSource URI
|
||||
| OwnSpec GHCupInfo
|
||||
deriving Show
|
||||
deriving (GHC.Generic, Show)
|
||||
|
||||
|
||||
data Settings = Settings
|
||||
{ cache :: Bool
|
||||
{ -- set by user
|
||||
cache :: Bool
|
||||
, noVerify :: Bool
|
||||
, keepDirs :: KeepDirs
|
||||
, downloader :: Downloader
|
||||
, verbose :: Bool
|
||||
|
||||
-- set on app start
|
||||
, dirs :: Dirs
|
||||
}
|
||||
deriving Show
|
||||
|
||||
data Dirs = Dirs
|
||||
{ baseDir :: Path Abs
|
||||
, binDir :: Path Abs
|
||||
, cacheDir :: Path Abs
|
||||
, logsDir :: Path Abs
|
||||
}
|
||||
deriving Show
|
||||
|
||||
data KeepDirs = Always
|
||||
| Errors
|
||||
@@ -201,6 +248,12 @@ data PlatformResult = PlatformResult
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
prettyPlatform :: PlatformResult -> String
|
||||
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Just v' }
|
||||
= show plat <> ", " <> show v'
|
||||
prettyPlatform PlatformResult { _platform = plat, _distroVersion = Nothing }
|
||||
= show plat
|
||||
|
||||
data PlatformRequest = PlatformRequest
|
||||
{ _rArch :: Architecture
|
||||
, _rPlatform :: Platform
|
||||
@@ -208,6 +261,13 @@ data PlatformRequest = PlatformRequest
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
prettyPfReq :: PlatformRequest -> String
|
||||
prettyPfReq (PlatformRequest arch plat ver) =
|
||||
prettyArch arch ++ "-" ++ prettyPlatfrom plat ++ pver
|
||||
where
|
||||
pver = case ver of
|
||||
Just v' -> "-" ++ (T.unpack $ prettyV v')
|
||||
Nothing -> ""
|
||||
|
||||
-- | A GHC identified by the target platform triple
|
||||
-- and the version.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
Module : GHCup.Types.JSON
|
||||
Description : GHCup JSON types/instances
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -24,6 +24,7 @@ module GHCup.Types.JSON where
|
||||
import GHCup.Types
|
||||
import GHCup.Utils.Prelude
|
||||
|
||||
import Control.Applicative ( (<|>) )
|
||||
import Data.Aeson
|
||||
import Data.Aeson.TH
|
||||
import Data.Aeson.Types
|
||||
@@ -53,6 +54,7 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Requir
|
||||
instance ToJSON Tag where
|
||||
toJSON Latest = String "Latest"
|
||||
toJSON Recommended = String "Recommended"
|
||||
toJSON Prerelease = String "Prerelease"
|
||||
toJSON (Base pvp'') = String ("base-" <> prettyPVP pvp'')
|
||||
toJSON (UnknownTag x ) = String (T.pack x)
|
||||
|
||||
@@ -60,6 +62,7 @@ instance FromJSON Tag where
|
||||
parseJSON = withText "Tag" $ \t -> case T.unpack t of
|
||||
"Latest" -> pure Latest
|
||||
"Recommended" -> pure Recommended
|
||||
"Prerelease" -> pure Prerelease
|
||||
('b' : 'a' : 's' : 'e' : '-' : ver') -> case pvp (T.pack ver') of
|
||||
Right x -> pure $ Base x
|
||||
Left e -> fail . show $ e
|
||||
@@ -191,3 +194,18 @@ instance FromJSON (Path Rel) where
|
||||
case parseRel d of
|
||||
Right x -> pure x
|
||||
Left e -> fail $ "Failure in HPath Rel (FromJSON)" <> show e
|
||||
|
||||
|
||||
instance ToJSON TarDir where
|
||||
toJSON (RealDir p) = toJSON p
|
||||
toJSON (RegexDir r) = object ["RegexDir" .= r]
|
||||
|
||||
instance FromJSON TarDir where
|
||||
parseJSON v = realDir v <|> regexDir v
|
||||
where
|
||||
realDir = withText "TarDir" $ \t -> do
|
||||
fp <- parseJSON (String t)
|
||||
pure (RealDir fp)
|
||||
regexDir = withObject "TarDir" $ \o -> do
|
||||
r <- o .: "RegexDir"
|
||||
pure $ RegexDir r
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Module : GHCup.Types.Optics
|
||||
Description : GHCup optics
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
Module : GHCup.Utils
|
||||
Description : GHCup domain specific utilities
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -36,7 +36,7 @@ import GHCup.Utils.Prelude
|
||||
import GHCup.Utils.String.QQ
|
||||
|
||||
#if !defined(TAR)
|
||||
import Codec.Archive
|
||||
import Codec.Archive hiding ( Directory )
|
||||
#endif
|
||||
import Control.Applicative
|
||||
import Control.Exception.Safe
|
||||
@@ -48,7 +48,9 @@ import Control.Monad.Logger
|
||||
import Control.Monad.Reader
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.Either
|
||||
import Data.Foldable
|
||||
import Data.List
|
||||
import Data.List.Split
|
||||
import Data.Maybe
|
||||
import Data.String.Interpolate
|
||||
import Data.Text ( Text )
|
||||
@@ -97,20 +99,24 @@ import qualified Text.Megaparsec as MP
|
||||
|
||||
|
||||
-- | The symlink destination of a ghc tool.
|
||||
ghcLinkDestination :: ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
||||
ghcLinkDestination :: (MonadReader Settings m, MonadThrow m, MonadIO m)
|
||||
=> ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
||||
-> GHCTargetVersion
|
||||
-> ByteString
|
||||
ghcLinkDestination tool ver =
|
||||
"../ghc/" <> E.encodeUtf8 (prettyTVer ver) <> "/bin/" <> tool
|
||||
-> m ByteString
|
||||
ghcLinkDestination tool ver = do
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
t <- parseRel tool
|
||||
ghcd <- ghcupGHCDir ver
|
||||
pure (relativeSymlink binDir (ghcd </> [rel|bin|] </> t))
|
||||
|
||||
|
||||
-- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.
|
||||
rmMinorSymlinks :: (MonadIO m, MonadLogger m) => GHCTargetVersion -> m ()
|
||||
rmMinorSymlinks :: (MonadReader Settings m, MonadIO m, MonadLogger m) => GHCTargetVersion -> m ()
|
||||
rmMinorSymlinks GHCTargetVersion {..} = do
|
||||
bindir <- liftIO $ ghcupBinDir
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
|
||||
files <- liftIO $ findFiles'
|
||||
bindir
|
||||
binDir
|
||||
( maybe mempty (\x -> MP.chunk (x <> "-")) _tvTarget
|
||||
*> parseUntil1 (MP.chunk $ prettyVer _tvVersion)
|
||||
*> (MP.chunk $ prettyVer _tvVersion)
|
||||
@@ -118,42 +124,41 @@ rmMinorSymlinks GHCTargetVersion {..} = do
|
||||
)
|
||||
|
||||
forM_ files $ \f -> do
|
||||
let fullF = (bindir </> f)
|
||||
let fullF = (binDir </> f)
|
||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||
|
||||
|
||||
-- | Removes the set ghc version for the given target, if any.
|
||||
rmPlain :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||
rmPlain :: (MonadReader Settings m, MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||
=> Maybe Text -- ^ target
|
||||
-> Excepts '[NotInstalled] m ()
|
||||
rmPlain target = do
|
||||
mtv <- ghcSet target
|
||||
Settings {dirs = Dirs {..}} <- lift ask
|
||||
mtv <- lift $ ghcSet target
|
||||
forM_ mtv $ \tv -> do
|
||||
files <- liftE $ ghcToolFiles tv
|
||||
bindir <- liftIO $ ghcupBinDir
|
||||
forM_ files $ \f -> do
|
||||
let fullF = (bindir </> f)
|
||||
let fullF = (binDir </> f)
|
||||
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||
-- old ghcup
|
||||
let hdc_file = (bindir </> [rel|haddock-ghc|])
|
||||
let hdc_file = (binDir </> [rel|haddock-ghc|])
|
||||
lift $ $(logDebug) [i|rm -f #{toFilePath hdc_file}|]
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteFile hdc_file
|
||||
|
||||
|
||||
-- | Remove the major GHC symlink, e.g. ghc-8.6.
|
||||
rmMajorSymlinks :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||
rmMajorSymlinks :: (MonadReader Settings m, MonadThrow m, MonadLogger m, MonadIO m)
|
||||
=> GHCTargetVersion
|
||||
-> m ()
|
||||
rmMajorSymlinks GHCTargetVersion {..} = do
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
(mj, mi) <- getMajorMinorV _tvVersion
|
||||
let v' = intToText mj <> "." <> intToText mi
|
||||
|
||||
bindir <- liftIO ghcupBinDir
|
||||
|
||||
files <- liftIO $ findFiles'
|
||||
bindir
|
||||
binDir
|
||||
( maybe mempty (\x -> MP.chunk (x <> "-")) _tvTarget
|
||||
*> parseUntil1 (MP.chunk v')
|
||||
*> MP.chunk v'
|
||||
@@ -161,7 +166,7 @@ rmMajorSymlinks GHCTargetVersion {..} = do
|
||||
)
|
||||
|
||||
forM_ files $ \f -> do
|
||||
let fullF = (bindir </> f)
|
||||
let fullF = (binDir </> f)
|
||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||
|
||||
@@ -174,59 +179,61 @@ rmMajorSymlinks GHCTargetVersion {..} = do
|
||||
|
||||
|
||||
-- | Whethe the given GHC versin is installed.
|
||||
ghcInstalled :: GHCTargetVersion -> IO Bool
|
||||
ghcInstalled :: (MonadIO m, MonadReader Settings m, MonadThrow m) => GHCTargetVersion -> m Bool
|
||||
ghcInstalled ver = do
|
||||
ghcdir <- ghcupGHCDir ver
|
||||
doesDirectoryExist ghcdir
|
||||
liftIO $ doesDirectoryExist ghcdir
|
||||
|
||||
|
||||
-- | Whether the given GHC version is installed from source.
|
||||
ghcSrcInstalled :: GHCTargetVersion -> IO Bool
|
||||
ghcSrcInstalled :: (MonadIO m, MonadReader Settings m, MonadThrow m) => GHCTargetVersion -> m Bool
|
||||
ghcSrcInstalled ver = do
|
||||
ghcdir <- ghcupGHCDir ver
|
||||
doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
||||
liftIO $ doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
||||
|
||||
|
||||
-- | Whether the given GHC version is set as the current.
|
||||
ghcSet :: (MonadThrow m, MonadIO m)
|
||||
ghcSet :: (MonadReader Settings m, MonadThrow m, MonadIO m)
|
||||
=> Maybe Text -- ^ the target of the GHC version, if any
|
||||
-- (e.g. armv7-unknown-linux-gnueabihf)
|
||||
-> m (Maybe GHCTargetVersion)
|
||||
ghcSet mtarget = do
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
ghc <- parseRel $ E.encodeUtf8 (maybe "ghc" (<> "-ghc") mtarget)
|
||||
ghcBin <- (</> ghc) <$> liftIO ghcupBinDir
|
||||
let ghcBin = binDir </> ghc
|
||||
|
||||
-- link destination is of the form ../ghc/<ver>/bin/ghc
|
||||
-- for old ghcup, it is ../ghc/<ver>/bin/ghc-<ver>
|
||||
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
||||
link <- readSymbolicLink $ toFilePath ghcBin
|
||||
Just <$> ghcLinkVersion link
|
||||
|
||||
ghcLinkVersion :: MonadThrow m => ByteString -> m GHCTargetVersion
|
||||
ghcLinkVersion bs = do
|
||||
t <- throwEither $ E.decodeUtf8' bs
|
||||
throwEither $ MP.parse parser "ghcLinkVersion" t
|
||||
where
|
||||
ghcLinkVersion :: MonadThrow m => ByteString -> m GHCTargetVersion
|
||||
ghcLinkVersion bs = do
|
||||
t <- throwEither $ E.decodeUtf8' bs
|
||||
throwEither $ MP.parse parser "" t
|
||||
where
|
||||
parser =
|
||||
MP.chunk "../ghc/"
|
||||
*> (do
|
||||
r <- parseUntil1 (MP.chunk "/")
|
||||
rest <- MP.getInput
|
||||
MP.setInput r
|
||||
x <- ghcTargetVerP
|
||||
MP.setInput rest
|
||||
pure x
|
||||
)
|
||||
<* MP.chunk "/"
|
||||
<* MP.takeRest
|
||||
<* MP.eof
|
||||
parser =
|
||||
(do
|
||||
_ <- parseUntil1 (MP.chunk "/ghc/")
|
||||
_ <- MP.chunk "/ghc/"
|
||||
r <- parseUntil1 (MP.chunk "/")
|
||||
rest <- MP.getInput
|
||||
MP.setInput r
|
||||
x <- ghcTargetVerP
|
||||
MP.setInput rest
|
||||
pure x
|
||||
)
|
||||
<* MP.chunk "/"
|
||||
<* MP.takeRest
|
||||
<* MP.eof
|
||||
|
||||
|
||||
-- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.
|
||||
-- If a dir cannot be parsed, returns left.
|
||||
getInstalledGHCs :: MonadIO m => m [Either (Path Rel) GHCTargetVersion]
|
||||
getInstalledGHCs :: (MonadReader Settings m, MonadIO m) => m [Either (Path Rel) GHCTargetVersion]
|
||||
getInstalledGHCs = do
|
||||
ghcdir <- liftIO $ ghcupGHCBaseDir
|
||||
ghcdir <- ghcupGHCBaseDir
|
||||
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ getDirsFiles' ghcdir
|
||||
forM fs $ \f -> case parseGHCupGHCDir f of
|
||||
Right r -> pure $ Right r
|
||||
@@ -234,46 +241,211 @@ getInstalledGHCs = do
|
||||
|
||||
|
||||
-- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@.
|
||||
getInstalledCabals :: IO [Either (Path Rel) Version]
|
||||
getInstalledCabals :: (MonadReader Settings m, MonadIO m, MonadCatch m)
|
||||
=> m [Either (Path Rel) Version]
|
||||
getInstalledCabals = do
|
||||
bindir <- liftIO $ ghcupBinDir
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
bindir
|
||||
binDir
|
||||
(makeRegexOpts compExtended execBlank ([s|^cabal-.*$|] :: ByteString))
|
||||
vs <- forM bins $ \f -> case fmap version (fmap decUTF8Safe . B.stripPrefix "cabal-" . toFilePath $ f) of
|
||||
Just (Right r) -> pure $ Right r
|
||||
Just (Left _) -> pure $ Left f
|
||||
Nothing -> pure $ Left f
|
||||
cs <- cabalSet -- for legacy cabal
|
||||
pure $ maybe vs (\x -> Right x:vs) cs
|
||||
pure $ maybe vs (\x -> nub $ Right x:vs) cs
|
||||
|
||||
|
||||
-- | Whether the given cabal version is installed.
|
||||
cabalInstalled :: Version -> IO Bool
|
||||
cabalInstalled :: (MonadIO m, MonadReader Settings m, MonadCatch m) => Version -> m Bool
|
||||
cabalInstalled ver = do
|
||||
vers <- fmap rights $ getInstalledCabals
|
||||
pure $ elem ver $ vers
|
||||
|
||||
|
||||
-- Return the currently set cabal version, if any.
|
||||
cabalSet :: (MonadIO m, MonadThrow m) => m (Maybe Version)
|
||||
cabalSet :: (MonadReader Settings m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
||||
cabalSet = do
|
||||
cabalbin <- (</> [rel|cabal|]) <$> liftIO ghcupBinDir
|
||||
mc <- liftIO $ handleIO (\_ -> pure Nothing) $ fmap Just $ executeOut
|
||||
cabalbin
|
||||
["--numeric-version"]
|
||||
Nothing
|
||||
fmap join $ forM mc $ \c -> if
|
||||
| not (B.null (_stdOut c))
|
||||
, _exitCode c == ExitSuccess -> do
|
||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ c
|
||||
case version $ decUTF8Safe reportedVer of
|
||||
Left e -> throwM e
|
||||
Right r -> pure $ Just r
|
||||
| otherwise -> pure Nothing
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
let cabalbin = binDir </> [rel|cabal|]
|
||||
b <- handleIO (\_ -> pure False) $ fmap (== SymbolicLink) $ liftIO $ getFileType cabalbin
|
||||
if
|
||||
| b -> do
|
||||
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
||||
broken <- isBrokenSymlink cabalbin
|
||||
if broken
|
||||
then pure Nothing
|
||||
else do
|
||||
link <- readSymbolicLink $ toFilePath cabalbin
|
||||
Just <$> linkVersion link
|
||||
| otherwise -> do -- legacy behavior
|
||||
mc <- liftIO $ handleIO (\_ -> pure Nothing) $ fmap Just $ executeOut
|
||||
cabalbin
|
||||
["--numeric-version"]
|
||||
Nothing
|
||||
fmap join $ forM mc $ \c -> if
|
||||
| not (B.null (_stdOut c)), _exitCode c == ExitSuccess -> do
|
||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ c
|
||||
case version $ decUTF8Safe reportedVer of
|
||||
Left e -> throwM e
|
||||
Right r -> pure $ Just r
|
||||
| otherwise -> pure Nothing
|
||||
where
|
||||
linkVersion :: MonadThrow m => ByteString -> m Version
|
||||
linkVersion bs = do
|
||||
t <- throwEither $ E.decodeUtf8' bs
|
||||
throwEither $ MP.parse parser "" t
|
||||
where
|
||||
parser =
|
||||
MP.chunk "cabal-" *> version'
|
||||
|
||||
|
||||
|
||||
-- | Get all installed hls, by matching on
|
||||
-- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@.
|
||||
getInstalledHLSs :: (MonadReader Settings m, MonadIO m, MonadCatch m)
|
||||
=> m [Either (Path Rel) Version]
|
||||
getInstalledHLSs = do
|
||||
Settings { dirs = Dirs {..} } <- ask
|
||||
bins <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
binDir
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^haskell-language-server-wrapper-.*$|] :: ByteString)
|
||||
)
|
||||
vs <- forM bins $ \f ->
|
||||
case
|
||||
fmap
|
||||
version
|
||||
(fmap decUTF8Safe . B.stripPrefix "haskell-language-server-wrapper-" . toFilePath $ f)
|
||||
of
|
||||
Just (Right r) -> pure $ Right r
|
||||
Just (Left _) -> pure $ Left f
|
||||
Nothing -> pure $ Left f
|
||||
pure $ vs
|
||||
|
||||
|
||||
-- | Whether the given HLS version is installed.
|
||||
hlsInstalled :: (MonadIO m, MonadReader Settings m, MonadCatch m) => Version -> m Bool
|
||||
hlsInstalled ver = do
|
||||
vers <- fmap rights $ getInstalledHLSs
|
||||
pure $ elem ver $ vers
|
||||
|
||||
|
||||
|
||||
-- Return the currently set hls version, if any.
|
||||
hlsSet :: (MonadReader Settings m, MonadIO m, MonadThrow m, MonadCatch m) => m (Maybe Version)
|
||||
hlsSet = do
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
let hlsBin = binDir </> [rel|haskell-language-server-wrapper|]
|
||||
|
||||
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
||||
broken <- isBrokenSymlink hlsBin
|
||||
if broken
|
||||
then pure Nothing
|
||||
else do
|
||||
link <- readSymbolicLink $ toFilePath hlsBin
|
||||
Just <$> linkVersion link
|
||||
where
|
||||
linkVersion :: MonadThrow m => ByteString -> m Version
|
||||
linkVersion bs = do
|
||||
t <- throwEither $ E.decodeUtf8' bs
|
||||
throwEither $ MP.parse parser "" t
|
||||
where
|
||||
parser =
|
||||
MP.chunk "haskell-language-server-wrapper-" *> version'
|
||||
|
||||
|
||||
-- | Return the GHC versions the currently selected HLS supports.
|
||||
hlsGHCVersions :: ( MonadReader Settings m
|
||||
, MonadIO m
|
||||
, MonadThrow m
|
||||
, MonadCatch m
|
||||
)
|
||||
=> m [Version]
|
||||
hlsGHCVersions = do
|
||||
h <- hlsSet
|
||||
vers <- forM h $ \h' -> do
|
||||
bins <- hlsServerBinaries h'
|
||||
pure $ fmap
|
||||
(\bin ->
|
||||
version
|
||||
. decUTF8Safe
|
||||
. fromJust
|
||||
. B.stripPrefix "haskell-language-server-"
|
||||
. head
|
||||
. B.split _tilde
|
||||
. toFilePath
|
||||
$ bin
|
||||
)
|
||||
bins
|
||||
pure . rights . concat . maybeToList $ vers
|
||||
|
||||
|
||||
-- | Get all server binaries for an hls version, if any.
|
||||
hlsServerBinaries :: (MonadReader Settings m, MonadIO m)
|
||||
=> Version
|
||||
-> m [Path Rel]
|
||||
hlsServerBinaries ver = do
|
||||
Settings { dirs = Dirs {..} } <- ask
|
||||
liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
binDir
|
||||
(makeRegexOpts
|
||||
compExtended
|
||||
execBlank
|
||||
([s|^haskell-language-server-.*~|] <> escapeVerRex ver <> [s|$|] :: ByteString
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
-- | Get the wrapper binary for an hls version, if any.
|
||||
hlsWrapperBinary :: (MonadReader Settings m, MonadThrow m, MonadIO m)
|
||||
=> Version
|
||||
-> m (Maybe (Path Rel))
|
||||
hlsWrapperBinary ver = do
|
||||
Settings { dirs = Dirs {..} } <- ask
|
||||
wrapper <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
binDir
|
||||
(makeRegexOpts
|
||||
compExtended
|
||||
execBlank
|
||||
([s|^haskell-language-server-wrapper-|] <> escapeVerRex ver <> [s|$|] :: ByteString
|
||||
)
|
||||
)
|
||||
case wrapper of
|
||||
[] -> pure $ Nothing
|
||||
[x] -> pure $ Just x
|
||||
_ -> throwM $ UnexpectedListLength
|
||||
"There were multiple hls wrapper binaries for a single version"
|
||||
|
||||
|
||||
-- | Get all binaries for an hls version, if any.
|
||||
hlsAllBinaries :: (MonadReader Settings m, MonadIO m, MonadThrow m) => Version -> m [Path Rel]
|
||||
hlsAllBinaries ver = do
|
||||
hls <- hlsServerBinaries ver
|
||||
wrapper <- hlsWrapperBinary ver
|
||||
pure (maybeToList wrapper ++ hls)
|
||||
|
||||
|
||||
-- | Get the active symlinks for hls.
|
||||
hlsSymlinks :: (MonadReader Settings m, MonadIO m, MonadCatch m) => m [Path Rel]
|
||||
hlsSymlinks = do
|
||||
Settings { dirs = Dirs {..} } <- ask
|
||||
oldSyms <- liftIO $ handleIO (\_ -> pure []) $ findFiles
|
||||
binDir
|
||||
(makeRegexOpts compExtended
|
||||
execBlank
|
||||
([s|^haskell-language-server-.*$|] :: ByteString)
|
||||
)
|
||||
filterM
|
||||
( fmap (== SymbolicLink)
|
||||
. liftIO
|
||||
. getFileType
|
||||
. (binDir </>)
|
||||
)
|
||||
oldSyms
|
||||
|
||||
|
||||
|
||||
-----------------------------------------
|
||||
--[ Major version introspection (X.Y) ]--
|
||||
@@ -295,7 +467,7 @@ matchMajor v' major' minor' = case getMajorMinorV v' of
|
||||
|
||||
-- | Get the latest installed full GHC version that satisfies X.Y.
|
||||
-- This reads `ghcupGHCBaseDir`.
|
||||
getGHCForMajor :: (MonadIO m, MonadThrow m)
|
||||
getGHCForMajor :: (MonadReader Settings m, MonadIO m, MonadThrow m)
|
||||
=> Int -- ^ major version component
|
||||
-> Int -- ^ minor version component
|
||||
-> Maybe Text -- ^ the target triple
|
||||
@@ -352,17 +524,16 @@ unpackToDir dest av = do
|
||||
#if defined(TAR)
|
||||
let untar :: MonadIO m => BL.ByteString -> Excepts '[] m ()
|
||||
untar = liftIO . Tar.unpack (toFilePath dest) . Tar.read
|
||||
|
||||
rf :: MonadIO m => Path Abs -> Excepts '[] m BL.ByteString
|
||||
rf = liftIO . readFile
|
||||
#else
|
||||
let untar :: MonadIO m => BL.ByteString -> Excepts '[ArchiveResult] m ()
|
||||
untar = lEM . liftIO . runArchiveM . unpackToDirLazy (T.unpack . decUTF8Safe . toFilePath $ dest)
|
||||
#endif
|
||||
|
||||
#if defined(TAR)
|
||||
rf :: MonadIO m => Path Abs -> Excepts '[] m BL.ByteString
|
||||
#else
|
||||
rf :: MonadIO m => Path Abs -> Excepts '[ArchiveResult] m BL.ByteString
|
||||
#endif
|
||||
rf = liftIO . readFile
|
||||
#endif
|
||||
|
||||
-- extract, depending on file extension
|
||||
if
|
||||
@@ -378,6 +549,28 @@ unpackToDir dest av = do
|
||||
| otherwise -> throwE $ UnknownArchive fn
|
||||
|
||||
|
||||
intoSubdir :: (MonadLogger m, MonadIO m, MonadThrow m, MonadCatch m)
|
||||
=> Path Abs -- ^ unpacked tar dir
|
||||
-> TarDir -- ^ how to descend
|
||||
-> Excepts '[TarDirDoesNotExist] m (Path Abs)
|
||||
intoSubdir bdir tardir = case tardir of
|
||||
RealDir pr -> do
|
||||
whenM (fmap not . liftIO . doesDirectoryExist $ (bdir </> pr))
|
||||
(throwE $ TarDirDoesNotExist tardir)
|
||||
pure (bdir </> pr)
|
||||
RegexDir r -> do
|
||||
let rs = splitOn "/" r
|
||||
foldlM
|
||||
(\y x ->
|
||||
(fmap sort . handleIO (\_ -> pure []) . liftIO . findFiles y . regex $ x) >>= \case
|
||||
[] -> throwE $ TarDirDoesNotExist tardir
|
||||
(p : _) -> pure (y </> p)
|
||||
)
|
||||
bdir
|
||||
rs
|
||||
where regex = makeRegexOpts compIgnoreCase execBlank
|
||||
|
||||
|
||||
|
||||
|
||||
------------
|
||||
@@ -440,11 +633,11 @@ urlBaseName = parseRel . snd . B.breakEnd (== _slash) . urlDecode False
|
||||
-- Returns unversioned relative files, e.g.:
|
||||
--
|
||||
-- - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@
|
||||
ghcToolFiles :: (MonadThrow m, MonadFail m, MonadIO m)
|
||||
ghcToolFiles :: (MonadReader Settings m, MonadThrow m, MonadFail m, MonadIO m)
|
||||
=> GHCTargetVersion
|
||||
-> Excepts '[NotInstalled] m [Path Rel]
|
||||
ghcToolFiles ver = do
|
||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||
ghcdir <- lift $ ghcupGHCDir ver
|
||||
let bindir = ghcdir </> [rel|bin|]
|
||||
|
||||
-- fail if ghc is not installed
|
||||
@@ -547,30 +740,24 @@ getChangeLog dls tool (Right tag) =
|
||||
-- 1. the build directory, depending on the KeepDirs setting
|
||||
-- 2. the install destination, depending on whether the build failed
|
||||
runBuildAction :: (Show (V e), MonadReader Settings m, MonadIO m, MonadMask m)
|
||||
=> Path Abs -- ^ build directory
|
||||
-> Maybe (Path Abs) -- ^ install location (e.g. for GHC)
|
||||
=> Path Abs -- ^ build directory (cleaned up depending on Settings)
|
||||
-> Maybe (Path Abs) -- ^ dir to *always* clean up on exception
|
||||
-> Excepts e m a
|
||||
-> Excepts '[BuildFailed] m a
|
||||
runBuildAction bdir instdir action = do
|
||||
Settings {..} <- lift ask
|
||||
v <- flip
|
||||
onException
|
||||
(do
|
||||
let exAction = do
|
||||
forM_ instdir $ \dir ->
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive dir
|
||||
when (keepDirs == Never)
|
||||
$ liftIO
|
||||
$ hideError doesNotExistErrorType
|
||||
$ deleteDirRecursive bdir
|
||||
)
|
||||
v <-
|
||||
flip onException exAction
|
||||
$ catchAllE
|
||||
(\es -> do
|
||||
forM_ instdir $ \dir ->
|
||||
liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive dir
|
||||
when (keepDirs == Never)
|
||||
$ liftIO
|
||||
$ hideError doesNotExistErrorType
|
||||
$ deleteDirRecursive bdir
|
||||
exAction
|
||||
throwE (BuildFailed bdir es)
|
||||
)
|
||||
$ action
|
||||
@@ -578,3 +765,25 @@ runBuildAction bdir instdir action = do
|
||||
when (keepDirs == Never || keepDirs == Errors) $ liftIO $ deleteDirRecursive
|
||||
bdir
|
||||
pure v
|
||||
|
||||
|
||||
-- | More permissive version of 'createDirRecursive'. This doesn't
|
||||
-- error when the destination is a symlink to a directory.
|
||||
createDirRecursive' :: Path b -> IO ()
|
||||
createDirRecursive' p =
|
||||
handleIO (\e -> if isAlreadyExistsError e then isSymlinkDir e else throwIO e)
|
||||
. createDirRecursive newDirPerms
|
||||
$ p
|
||||
|
||||
where
|
||||
isSymlinkDir e = do
|
||||
ft <- getFileType p
|
||||
case ft of
|
||||
SymbolicLink -> do
|
||||
rp <- canonicalizePath p
|
||||
rft <- getFileType rp
|
||||
case rft of
|
||||
Directory -> pure ()
|
||||
_ -> throwIO e
|
||||
_ -> throwIO e
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
@@ -6,12 +7,21 @@
|
||||
Module : GHCup.Utils.Dirs
|
||||
Description : Definition of GHCup directories
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
-}
|
||||
module GHCup.Utils.Dirs where
|
||||
module GHCup.Utils.Dirs
|
||||
( getDirs
|
||||
, ghcupGHCBaseDir
|
||||
, ghcupGHCDir
|
||||
, parseGHCupGHCDir
|
||||
, mkGhcupTmpDir
|
||||
, withGHCupTmpDir
|
||||
, relativeSymlink
|
||||
)
|
||||
where
|
||||
|
||||
|
||||
import GHCup.Types
|
||||
@@ -24,6 +34,7 @@ import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.Resource
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.Maybe
|
||||
import HPath
|
||||
import HPath.IO
|
||||
@@ -35,6 +46,7 @@ import Prelude hiding ( abs
|
||||
import System.Posix.Env.ByteString ( getEnv
|
||||
, getEnvDefault
|
||||
)
|
||||
import System.Posix.FilePath hiding ( (</>) )
|
||||
import System.Posix.Temp.ByteString ( mkdtemp )
|
||||
|
||||
import qualified Data.ByteString.UTF8 as UTF8
|
||||
@@ -45,33 +57,117 @@ import qualified Text.Megaparsec as MP
|
||||
|
||||
|
||||
|
||||
------------------------------
|
||||
--[ GHCup base directories ]--
|
||||
------------------------------
|
||||
|
||||
|
||||
-- | ~/.ghcup by default
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.
|
||||
ghcupBaseDir :: IO (Path Abs)
|
||||
ghcupBaseDir = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
then do
|
||||
bdir <- getEnv "XDG_DATA_HOME" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> [rel|.local/share|])
|
||||
pure (bdir </> [rel|ghcup|])
|
||||
else do
|
||||
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> liftIO getHomeDirectory
|
||||
pure (bdir </> [rel|.ghcup|])
|
||||
|
||||
|
||||
-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'
|
||||
-- (which, sadly is not strictly xdg spec).
|
||||
ghcupBinDir :: IO (Path Abs)
|
||||
ghcupBinDir = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
then do
|
||||
getEnv "XDG_BIN_HOME" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> [rel|.local/bin|])
|
||||
else ghcupBaseDir <&> (</> [rel|bin|])
|
||||
|
||||
|
||||
-- | Defaults to '~/.ghcup/cache'.
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.
|
||||
ghcupCacheDir :: IO (Path Abs)
|
||||
ghcupCacheDir = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
then do
|
||||
bdir <- getEnv "XDG_CACHE_HOME" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> [rel|.cache|])
|
||||
pure (bdir </> [rel|ghcup|])
|
||||
else ghcupBaseDir <&> (</> [rel|cache|])
|
||||
|
||||
|
||||
-- | Defaults to '~/.ghcup/logs'.
|
||||
--
|
||||
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
|
||||
-- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.
|
||||
ghcupLogsDir :: IO (Path Abs)
|
||||
ghcupLogsDir = do
|
||||
xdg <- useXDG
|
||||
if xdg
|
||||
then do
|
||||
bdir <- getEnv "XDG_CACHE_HOME" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> do
|
||||
home <- liftIO getHomeDirectory
|
||||
pure (home </> [rel|.cache|])
|
||||
pure (bdir </> [rel|ghcup/logs|])
|
||||
else ghcupBaseDir <&> (</> [rel|logs|])
|
||||
|
||||
|
||||
getDirs :: IO Dirs
|
||||
getDirs = do
|
||||
baseDir <- ghcupBaseDir
|
||||
binDir <- ghcupBinDir
|
||||
cacheDir <- ghcupCacheDir
|
||||
logsDir <- ghcupLogsDir
|
||||
pure Dirs { .. }
|
||||
|
||||
|
||||
|
||||
-------------------------
|
||||
--[ GHCup directories ]--
|
||||
-------------------------
|
||||
|
||||
|
||||
-- | ~/.ghcup by default
|
||||
ghcupBaseDir :: IO (Path Abs)
|
||||
ghcupBaseDir = do
|
||||
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||
Just r -> parseAbs r
|
||||
Nothing -> liftIO getHomeDirectory
|
||||
pure (bdir </> [rel|.ghcup|])
|
||||
|
||||
|
||||
-- | ~/.ghcup/ghc by default.
|
||||
ghcupGHCBaseDir :: IO (Path Abs)
|
||||
ghcupGHCBaseDir = ghcupBaseDir <&> (</> [rel|ghc|])
|
||||
ghcupGHCBaseDir :: (MonadReader Settings m) => m (Path Abs)
|
||||
ghcupGHCBaseDir = do
|
||||
Settings {..} <- ask
|
||||
pure (baseDir dirs </> [rel|ghc|])
|
||||
|
||||
|
||||
-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.
|
||||
-- The dir may be of the form
|
||||
-- * armv7-unknown-linux-gnueabihf-8.8.3
|
||||
-- * 8.8.4
|
||||
ghcupGHCDir :: GHCTargetVersion -> IO (Path Abs)
|
||||
ghcupGHCDir :: (MonadReader Settings m, MonadThrow m)
|
||||
=> GHCTargetVersion
|
||||
-> m (Path Abs)
|
||||
ghcupGHCDir ver = do
|
||||
ghcbasedir <- ghcupGHCBaseDir
|
||||
verdir <- parseRel $ E.encodeUtf8 (prettyTVer ver)
|
||||
ghcbasedir <- ghcupGHCBaseDir
|
||||
verdir <- parseRel $ E.encodeUtf8 (prettyTVer ver)
|
||||
pure (ghcbasedir </> verdir)
|
||||
|
||||
|
||||
@@ -82,16 +178,6 @@ parseGHCupGHCDir (toFilePath -> f) = do
|
||||
throwEither $ MP.parse ghcTargetVerP "" fp
|
||||
|
||||
|
||||
ghcupBinDir :: IO (Path Abs)
|
||||
ghcupBinDir = ghcupBaseDir <&> (</> [rel|bin|])
|
||||
|
||||
ghcupCacheDir :: IO (Path Abs)
|
||||
ghcupCacheDir = ghcupBaseDir <&> (</> [rel|cache|])
|
||||
|
||||
ghcupLogsDir :: IO (Path Abs)
|
||||
ghcupLogsDir = ghcupBaseDir <&> (</> [rel|logs|])
|
||||
|
||||
|
||||
mkGhcupTmpDir :: (MonadThrow m, MonadIO m) => m (Path Abs)
|
||||
mkGhcupTmpDir = do
|
||||
tmpdir <- liftIO $ getEnvDefault "TMPDIR" "/tmp"
|
||||
@@ -103,6 +189,8 @@ withGHCupTmpDir :: (MonadResource m, MonadThrow m, MonadIO m) => m (Path Abs)
|
||||
withGHCupTmpDir = snd <$> allocate mkGhcupTmpDir deleteDirRecursive
|
||||
|
||||
|
||||
|
||||
|
||||
--------------
|
||||
--[ Others ]--
|
||||
--------------
|
||||
@@ -116,3 +204,23 @@ getHomeDirectory = do
|
||||
Nothing -> do
|
||||
h <- PU.homeDirectory <$> (PU.getEffectiveUserID >>= PU.getUserEntryForID)
|
||||
parseAbs $ UTF8.fromString h -- this is a guess
|
||||
|
||||
|
||||
useXDG :: IO Bool
|
||||
useXDG = isJust <$> getEnv "GHCUP_USE_XDG_DIRS"
|
||||
|
||||
|
||||
relativeSymlink :: Path Abs -- ^ the path in which to create the symlink
|
||||
-> Path Abs -- ^ the symlink destination
|
||||
-> ByteString
|
||||
relativeSymlink (toFilePath -> p1) (toFilePath -> p2) =
|
||||
let d1 = splitDirectories p1
|
||||
d2 = splitDirectories p2
|
||||
common = takeWhile (\(x, y) -> x == y) $ zip d1 d2
|
||||
cPrefix = drop (length common) d1
|
||||
in joinPath (replicate (length cPrefix) "..")
|
||||
<> joinPath ("/" : (drop (length common) d2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Utils.File
|
||||
Description : File and unix APIs
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -17,7 +18,6 @@ Some of these functions use sophisticated logging.
|
||||
-}
|
||||
module GHCup.Utils.File where
|
||||
|
||||
import GHCup.Utils.Dirs
|
||||
import GHCup.Utils.Prelude
|
||||
import GHCup.Types
|
||||
|
||||
@@ -26,6 +26,7 @@ import Control.Concurrent.Async
|
||||
import Control.Exception ( evaluate )
|
||||
import Control.Exception.Safe
|
||||
import Control.Monad
|
||||
import Control.Monad.Logger
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Trans.State.Strict
|
||||
import Data.ByteString ( ByteString )
|
||||
@@ -34,6 +35,7 @@ import Data.Functor
|
||||
import Data.IORef
|
||||
import Data.Maybe
|
||||
import Data.Sequence ( Seq, (|>) )
|
||||
import Data.String.Interpolate
|
||||
import Data.Text ( Text )
|
||||
import Data.Void
|
||||
import Data.Word8
|
||||
@@ -47,6 +49,7 @@ import System.IO.Error
|
||||
import System.Posix.Directory.ByteString
|
||||
import System.Posix.FD as FD
|
||||
import System.Posix.FilePath hiding ( (</>) )
|
||||
import System.Posix.Files.ByteString
|
||||
import System.Posix.Foreign ( oExcl )
|
||||
import "unix" System.Posix.IO.ByteString
|
||||
hiding ( openFd )
|
||||
@@ -123,9 +126,8 @@ execLogged :: (MonadReader Settings m, MonadIO m, MonadThrow m)
|
||||
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
||||
-> m (Either ProcessError ())
|
||||
execLogged exe spath args lfile chdir env = do
|
||||
Settings {..} <- ask
|
||||
ldir <- liftIO ghcupLogsDir
|
||||
logfile <- (ldir </>) <$> parseRel (toFilePath lfile <> ".log")
|
||||
Settings {dirs = Dirs {..}, ..} <- ask
|
||||
logfile <- (logsDir </>) <$> parseRel (toFilePath lfile <> ".log")
|
||||
liftIO $ bracket (createFile (toFilePath logfile) newFilePerms)
|
||||
closeFd
|
||||
(action verbose)
|
||||
@@ -377,7 +379,7 @@ toProcessError :: ByteString
|
||||
-> Maybe ProcessStatus
|
||||
-> Either ProcessError ()
|
||||
toProcessError exe args mps = case mps of
|
||||
Just (SPPB.Exited (ExitFailure i)) -> Left $ NonZeroExit i exe args
|
||||
Just (SPPB.Exited (ExitFailure xi)) -> Left $ NonZeroExit xi exe args
|
||||
Just (SPPB.Exited ExitSuccess ) -> Right ()
|
||||
Just (Terminated _ _ ) -> Left $ PTerminated exe args
|
||||
Just (Stopped _ ) -> Left $ PStopped exe args
|
||||
@@ -427,3 +429,24 @@ findFiles' path parser = do
|
||||
Right p' -> isJust $ MP.parseMaybe parser p')
|
||||
$ dirContentsStream dirStream
|
||||
pure $ join $ fmap parseRel f
|
||||
|
||||
|
||||
isBrokenSymlink :: Path Abs -> IO Bool
|
||||
isBrokenSymlink p =
|
||||
handleIO
|
||||
(\e -> if ioeGetErrorType e == NoSuchThing then pure True else throwIO e)
|
||||
$ do
|
||||
_ <- canonicalizePath p
|
||||
pure False
|
||||
|
||||
|
||||
chmod_777 :: (MonadLogger m, MonadIO m) => Path a -> m ()
|
||||
chmod_777 (toFilePath -> fp) = do
|
||||
let exe_mode =
|
||||
newFilePerms
|
||||
`unionFileModes` ownerExecuteMode
|
||||
`unionFileModes` groupExecuteMode
|
||||
`unionFileModes` otherExecuteMode
|
||||
$(logDebug) [i|chmod 777 #{fp}|]
|
||||
liftIO $ setFileMode fp exe_mode
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
|
||||
{-|
|
||||
Module : GHCup.Utils.Logger
|
||||
Description : logger definition
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -13,9 +14,12 @@ Here we define our main logger.
|
||||
-}
|
||||
module GHCup.Utils.Logger where
|
||||
|
||||
import GHCup.Types
|
||||
import GHCup.Utils
|
||||
|
||||
import Control.Monad
|
||||
import Control.Monad.IO.Class
|
||||
import Control.Monad.Reader
|
||||
import Control.Monad.Logger
|
||||
import HPath
|
||||
import HPath.IO
|
||||
@@ -61,11 +65,12 @@ myLoggerT LoggerConfig {..} loggingt = runLoggingT loggingt mylogger
|
||||
rawOutter outr
|
||||
|
||||
|
||||
initGHCupFileLogging :: Path Rel -> IO (Path Abs)
|
||||
initGHCupFileLogging :: (MonadIO m, MonadReader Settings m) => Path Rel -> m (Path Abs)
|
||||
initGHCupFileLogging context = do
|
||||
logs <- ghcupLogsDir
|
||||
let logfile = logs </> context
|
||||
createDirIfMissing newDirPerms logs
|
||||
hideError doesNotExistErrorType $ deleteFile logfile
|
||||
createRegularFile newFilePerms logfile
|
||||
pure logfile
|
||||
Settings {dirs = Dirs {..}} <- ask
|
||||
let logfile = logsDir </> context
|
||||
liftIO $ do
|
||||
createDirRecursive' logsDir
|
||||
hideError doesNotExistErrorType $ deleteFile logfile
|
||||
createRegularFile newFilePerms logfile
|
||||
pure logfile
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Module : GHCup.Utils.MegaParsec
|
||||
Description : MegaParsec utilities
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
Module : GHCup.Utils.Prelude
|
||||
Description : MegaParsec utilities
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -31,11 +31,13 @@ import Data.ByteString ( ByteString )
|
||||
import Data.String
|
||||
import Data.Text ( Text )
|
||||
import Data.Versions
|
||||
import Data.Word8
|
||||
import Haskus.Utils.Types.List
|
||||
import Haskus.Utils.Variant.Excepts
|
||||
import System.IO.Error
|
||||
import System.Posix.Env.ByteString ( getEnvironment )
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.Strict.Maybe as S
|
||||
import qualified Data.Text as T
|
||||
@@ -275,3 +277,13 @@ decUTF8Safe = E.decodeUtf8With E.lenientDecode
|
||||
|
||||
decUTF8Safe' :: L.ByteString -> Text
|
||||
decUTF8Safe' = TL.toStrict . TLE.decodeUtf8With E.lenientDecode
|
||||
|
||||
|
||||
-- | Escape a version for use in regex
|
||||
escapeVerRex :: Version -> ByteString
|
||||
escapeVerRex = B.pack . go . B.unpack . verToBS
|
||||
where
|
||||
go [] = []
|
||||
go (x : xs) | x == _period = [_backslash, _period] ++ go xs
|
||||
| otherwise = x : go xs
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Module : GHCup.Utils.String.QQ
|
||||
Description : String quasi quoters
|
||||
Copyright : (c) Audrey Tang <audreyt@audreyt.org> 2019, Julian Ospald <hasufell@posteo.de> 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
Module : GHCup.Utils.Version.QQ
|
||||
Description : Version quasi-quoters
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
Module : GHCup.Version
|
||||
Description : Static version information
|
||||
Copyright : (c) Julian Ospald, 2020
|
||||
License : GPL-3
|
||||
License : LGPL-3.0
|
||||
Maintainer : hasufell@hasufell.de
|
||||
Stability : experimental
|
||||
Portability : POSIX
|
||||
@@ -20,13 +20,13 @@ import URI.ByteString.QQ
|
||||
|
||||
import qualified Data.Text as T
|
||||
|
||||
-- | This reflects the API version of the JSON.
|
||||
-- | This reflects the API version of the YAML.
|
||||
ghcupURL :: URI
|
||||
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.2.json|]
|
||||
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.2.yaml|]
|
||||
|
||||
-- | The curren ghcup version.
|
||||
-- | The current ghcup version.
|
||||
ghcUpVer :: PVP
|
||||
ghcUpVer = [pver|0.1.8|]
|
||||
ghcUpVer = [pver|0.1.11|]
|
||||
|
||||
-- | ghcup version as numeric string.
|
||||
numericVer :: String
|
||||
|
||||
193
test/GHCup/ArbitraryTypes.hs
Normal file
193
test/GHCup/ArbitraryTypes.hs
Normal file
@@ -0,0 +1,193 @@
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE StandaloneDeriving #-}
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module GHCup.ArbitraryTypes where
|
||||
|
||||
|
||||
import GHCup.Types
|
||||
|
||||
import Data.ByteString ( ByteString )
|
||||
import Data.Versions
|
||||
import Data.List.NonEmpty
|
||||
import HPath
|
||||
import Test.QuickCheck
|
||||
import Test.QuickCheck.Arbitrary.ADT ( ToADTArbitrary )
|
||||
import Test.QuickCheck.Arbitrary.Generic
|
||||
import URI.ByteString
|
||||
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as E
|
||||
import qualified Data.Text.Lazy as T
|
||||
( toStrict )
|
||||
import qualified Data.Text.Lazy.Builder as B
|
||||
import qualified Data.Text.Lazy.Builder.Int as B
|
||||
|
||||
|
||||
-----------------
|
||||
--[ utilities ]--
|
||||
-----------------
|
||||
|
||||
intToText :: Integral a => a -> T.Text
|
||||
intToText = T.toStrict . B.toLazyText . B.decimal
|
||||
|
||||
genVer :: Gen (Int, Int, Int)
|
||||
genVer =
|
||||
(\x y z -> (getPositive x, getPositive y, getPositive z))
|
||||
<$> arbitrary
|
||||
<*> arbitrary
|
||||
<*> arbitrary
|
||||
|
||||
|
||||
instance ToADTArbitrary GHCupInfo
|
||||
|
||||
|
||||
|
||||
----------------------
|
||||
--[ base arbitrary ]--
|
||||
----------------------
|
||||
|
||||
instance Arbitrary T.Text where
|
||||
arbitrary = fmap T.pack $ listOf $ elements ['a' .. 'z']
|
||||
shrink xs = T.pack <$> shrink (T.unpack xs)
|
||||
|
||||
instance Arbitrary (NonEmpty Word) where
|
||||
arbitrary = fmap fromList $ listOf1 $ arbitrary
|
||||
|
||||
-- utf8 encoded bytestring
|
||||
instance Arbitrary ByteString where
|
||||
arbitrary = fmap (E.encodeUtf8 . T.pack) $ listOf $ elements ['a' .. 'z']
|
||||
|
||||
|
||||
|
||||
---------------------
|
||||
--[ uri arbitrary ]--
|
||||
---------------------
|
||||
|
||||
instance Arbitrary Scheme where
|
||||
arbitrary = oneof [ Scheme <$> pure "http", Scheme <$> pure "https" ]
|
||||
|
||||
instance Arbitrary Host where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Port where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary (URIRef Absolute) where
|
||||
arbitrary =
|
||||
URI <$> arbitrary <*> pure Nothing <*> arbitrary <*> (Query <$> pure []) <*> pure Nothing
|
||||
|
||||
|
||||
|
||||
-------------------------
|
||||
--[ version arbitrary ]--
|
||||
-------------------------
|
||||
|
||||
instance Arbitrary Mess where
|
||||
arbitrary = do
|
||||
(x, y, z) <- genVer
|
||||
pure
|
||||
$ either (error . show) id
|
||||
$ mess
|
||||
$ (intToText x <> "." <> intToText y <> "." <> intToText z)
|
||||
|
||||
instance Arbitrary Version where
|
||||
arbitrary = do
|
||||
(x, y, z) <- genVer
|
||||
pure
|
||||
$ either (error . show) id
|
||||
$ version
|
||||
$ (intToText x <> "." <> intToText y <> "." <> intToText z)
|
||||
|
||||
instance Arbitrary SemVer where
|
||||
arbitrary = do
|
||||
(x, y, z) <- genVer
|
||||
pure
|
||||
$ either (error . show) id
|
||||
$ semver
|
||||
$ (intToText x <> "." <> intToText y <> "." <> intToText z)
|
||||
|
||||
instance Arbitrary PVP where
|
||||
arbitrary = do
|
||||
(x, y, z) <- genVer
|
||||
pure
|
||||
$ either (error . show) id
|
||||
$ pvp
|
||||
$ (intToText x <> "." <> intToText y <> "." <> intToText z)
|
||||
|
||||
instance Arbitrary Versioning where
|
||||
arbitrary = Ideal <$> arbitrary
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
--[ ghcup arbitrary ]--
|
||||
-----------------------
|
||||
|
||||
instance Arbitrary Requirements where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary DownloadInfo where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary LinuxDistro where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Platform where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Tag where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Architecture where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary VersionInfo where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary (Path Rel) where
|
||||
arbitrary =
|
||||
(either (error . show) id . parseRel . E.encodeUtf8 . T.pack)
|
||||
<$> (listOf1 $ elements ['a' .. 'z'])
|
||||
|
||||
instance Arbitrary TarDir where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary Tool where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
instance Arbitrary GHCupInfo where
|
||||
arbitrary = genericArbitrary
|
||||
shrink = genericShrink
|
||||
|
||||
|
||||
-- our maps are nested... the default size easily blows up most ppls ram
|
||||
|
||||
instance {-# OVERLAPS #-} Arbitrary v => Arbitrary (M.Map Tool v) where
|
||||
arbitrary = resize 8 $ M.fromList <$> arbitrary
|
||||
|
||||
instance {-# OVERLAPS #-} Arbitrary v => Arbitrary (M.Map (Maybe Version) v) where
|
||||
arbitrary = resize 8 $ M.fromList <$> arbitrary
|
||||
|
||||
instance {-# OVERLAPS #-} Arbitrary v => Arbitrary (M.Map Platform v) where
|
||||
arbitrary = resize 8 $ M.fromList <$> arbitrary
|
||||
|
||||
instance {-# OVERLAPS #-} Arbitrary v => Arbitrary (M.Map (Maybe Versioning) v) where
|
||||
arbitrary = resize 8 $ M.fromList <$> arbitrary
|
||||
|
||||
17
test/GHCup/Types/JSONSpec.hs
Normal file
17
test/GHCup/Types/JSONSpec.hs
Normal file
@@ -0,0 +1,17 @@
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module GHCup.Types.JSONSpec where
|
||||
|
||||
import GHCup.ArbitraryTypes ()
|
||||
import GHCup.Types
|
||||
import GHCup.Types.JSON ()
|
||||
|
||||
import Test.Aeson.GenericSpecs
|
||||
import Test.Hspec
|
||||
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
roundtripAndGoldenSpecs (Proxy @GHCupInfo)
|
||||
12
test/Main.hs
Normal file
12
test/Main.hs
Normal file
@@ -0,0 +1,12 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
import Test.Hspec.Runner
|
||||
import Test.Hspec.Formatters
|
||||
import qualified Spec
|
||||
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
hspecWith
|
||||
defaultConfig { configFormatter = Just progress }
|
||||
$ Spec.spec
|
||||
@@ -1,4 +0,0 @@
|
||||
module Main (main) where
|
||||
|
||||
main :: IO ()
|
||||
main = putStrLn "Test suite not yet implemented."
|
||||
2
test/Spec.hs
Normal file
2
test/Spec.hs
Normal file
@@ -0,0 +1,2 @@
|
||||
-- file test/Spec.hs
|
||||
{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
|
||||
Reference in New Issue
Block a user