Compare commits
29 Commits
ghcup-0.0.
...
v0.1.5
| Author | SHA1 | Date | |
|---|---|---|---|
| a8be2efd85 | |||
| f46700e1cc | |||
| d7a6935a1a | |||
| a1282b2854 | |||
| 34b9ea7d20 | |||
| 0ff7ebb1fd | |||
| f83dcbc430 | |||
| 56e4a6b15f | |||
| ee9b2ec30d | |||
| 640cf1e2c1 | |||
| 56c439d716 | |||
| 1ed6e49a81 | |||
| fad9f83e6a | |||
| 2e28b0d00f | |||
| ed4ff15f96 | |||
| 1d623723a2 | |||
| 931080244f | |||
| 27e2e7f848 | |||
| 8b638c7ecb | |||
| acd370611f | |||
| e1b5a89cee | |||
| 5edebd57d9 | |||
| bcaccaaf31 | |||
| 818a5d2d85 | |||
| 13acce07d4 | |||
| 4ed5e21b7f | |||
| 86aab6bb59 | |||
| 7f5cb64b18 | |||
| 6c12eb16eb |
@@ -57,7 +57,7 @@ variables:
|
|||||||
script:
|
script:
|
||||||
- ./.gitlab/script/ghcup_version.sh
|
- ./.gitlab/script/ghcup_version.sh
|
||||||
variables:
|
variables:
|
||||||
JSON_VERSION: "0.0.1"
|
JSON_VERSION: "0.0.2"
|
||||||
|
|
||||||
.test_ghcup_version:linux:
|
.test_ghcup_version:linux:
|
||||||
extends:
|
extends:
|
||||||
@@ -161,7 +161,7 @@ release:linux:32bit:
|
|||||||
before_script:
|
before_script:
|
||||||
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
||||||
variables:
|
variables:
|
||||||
ARTIFACT: "x86_64-linux-ghcup"
|
ARTIFACT: "i386-linux-ghcup"
|
||||||
GHC_VERSION: "8.8.3"
|
GHC_VERSION: "8.8.3"
|
||||||
CABAL_VERSION: "3.2.0.0"
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ chmod +x "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
|||||||
|
|
||||||
# utils
|
# utils
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
bash
|
bash \
|
||||||
|
git
|
||||||
|
|
||||||
## Package specific
|
## Package specific
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
set -eux
|
set -eux
|
||||||
|
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev
|
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev git wget
|
||||||
|
|
||||||
. "$( cd "$(dirname "$0")" ; pwd -P )/../../ghcup_env"
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../../ghcup_env"
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,17 @@ ecabal() {
|
|||||||
cabal --store-dir="$(pwd)"/.store "$@"
|
cabal --store-dir="$(pwd)"/.store "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git describe
|
||||||
|
|
||||||
# build
|
# build
|
||||||
ecabal update
|
ecabal update
|
||||||
|
|
||||||
if [ "${OS}" = "LINUX" ] ; then
|
if [ "${OS}" = "LINUX" ] ; then
|
||||||
ecabal build -w ghc-${GHC_VERSION} -fcurl --ghc-options='-split-sections -optl-static'
|
ecabal build -w ghc-${GHC_VERSION} --ghc-options='-split-sections -optl-static'
|
||||||
|
elif [ "${OS}" = "FREEBSD" ] ; then
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} --ghc-options='-split-sections'
|
||||||
else
|
else
|
||||||
ecabal build -w ghc-${GHC_VERSION} -fcurl
|
ecabal build -w ghc-${GHC_VERSION}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir out
|
mkdir out
|
||||||
|
|||||||
@@ -14,15 +14,16 @@ eghcup() {
|
|||||||
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.json "$@"
|
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.json "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git describe
|
||||||
|
|
||||||
### build
|
### build
|
||||||
|
|
||||||
ecabal update
|
ecabal update
|
||||||
|
|
||||||
if [ "${OS}" = "DARWIN" ] ; then
|
if [ "${OS}" = "DARWIN" ] ; then
|
||||||
ecabal build -w ghc-${GHC_VERSION} -fcurl
|
|
||||||
else
|
|
||||||
ecabal build -w ghc-${GHC_VERSION}
|
ecabal build -w ghc-${GHC_VERSION}
|
||||||
|
else
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} -finternal-downloader
|
||||||
fi
|
fi
|
||||||
|
|
||||||
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')" .
|
||||||
@@ -70,7 +71,11 @@ ghci-$(ghc --numeric-version) --version
|
|||||||
|
|
||||||
# test installing new ghc doesn't mess with currently set GHC
|
# test installing new ghc doesn't mess with currently set GHC
|
||||||
# https://gitlab.haskell.org/haskell/ghcup-hs/issues/7
|
# https://gitlab.haskell.org/haskell/ghcup-hs/issues/7
|
||||||
eghcup install 8.4.4
|
if [ "${OS}" = "LINUX" ] ; then
|
||||||
|
eghcup --downloader=wget install 8.4.4
|
||||||
|
else # test wget a bit
|
||||||
|
eghcup install 8.4.4
|
||||||
|
fi
|
||||||
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||||
eghcup set 8.4.4
|
eghcup set 8.4.4
|
||||||
eghcup set 8.4.4
|
eghcup set 8.4.4
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
|||||||
# Revision history for ghcup
|
# Revision history for ghcup
|
||||||
|
|
||||||
|
## 0.1.5 -- 2020-04-30
|
||||||
|
|
||||||
|
* Fix errors when PATH variable contains path components that are actually files
|
||||||
|
* Add `--version` and `--numeric-version` options
|
||||||
|
* Add `changelog` command
|
||||||
|
* Also check for available GHC and Cabal updates on start
|
||||||
|
* Add base versions as tags for every GHC version (these are "installable" tags and the latest GHC version matching the tag will be picked)
|
||||||
|
* Added `--format-raw` to list subcommand
|
||||||
|
* Allow to install X.Y versions (e.g.: ghcup install 8.8)
|
||||||
|
* Implement `--keep=<always|errors|never>` to control temporary build directories cleanup
|
||||||
|
* Add proper shell completions to the repo
|
||||||
|
* Fix building of documentation
|
||||||
|
* Allow to work in offline mode and use cached files if possible
|
||||||
|
* Allow to set the downloader via `--downloader=<curl|wget>`
|
||||||
|
* Support for compiling and installing a cross GHC (see README). This is experimental.
|
||||||
|
|
||||||
## 0.1.4 -- 2020-04-16
|
## 0.1.4 -- 2020-04-16
|
||||||
|
|
||||||
* build on all platforms with curl (as a binary), wrt https://gitlab.haskell.org/haskell/ghcup-hs/issues/6
|
* build on all platforms with curl (as a binary), wrt https://gitlab.haskell.org/haskell/ghcup-hs/issues/6
|
||||||
|
|||||||
19
HACKING.md
19
HACKING.md
@@ -43,3 +43,22 @@ Kazu Yamamoto [explained it in his PR](https://github.com/yesodweb/wai/pull/752#
|
|||||||
1. Brittany
|
1. Brittany
|
||||||
2. mtl-style preferred
|
2. mtl-style preferred
|
||||||
3. no overly pointfree style
|
3. no overly pointfree style
|
||||||
|
|
||||||
|
## Code structure
|
||||||
|
|
||||||
|
Main functionality is in `GHCup` module. Utility functions are
|
||||||
|
organised tree-ish in `GHCup.Utils` and `GHCup.Utils.*`.
|
||||||
|
|
||||||
|
Anything dealing with ghcup specific directories is in
|
||||||
|
`GHCup.Utils.Dirs`.
|
||||||
|
|
||||||
|
Download information on where to fetch bindists from is in
|
||||||
|
`GHCup.Data.GHCupDownloads`.
|
||||||
|
|
||||||
|
## Major refactors
|
||||||
|
|
||||||
|
1. First major refactor included adding cross support. This added
|
||||||
|
`GHCTargetVersion`, which includes the target in addition to the version.
|
||||||
|
Most of the `Version` parameters to functions had to be replaced with
|
||||||
|
that and ensured the logic is consistent for cross and non-cross
|
||||||
|
installs.
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -11,6 +11,8 @@ Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [p
|
|||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
* [Usage](#usage)
|
* [Usage](#usage)
|
||||||
* [Manpages](#manpages)
|
* [Manpages](#manpages)
|
||||||
|
* [Shell-completion](#shell-completion)
|
||||||
|
* [Cross support](#cross-support)
|
||||||
* [Design goals](#design-goals)
|
* [Design goals](#design-goals)
|
||||||
* [How](#how)
|
* [How](#how)
|
||||||
* [Known users](#known-users)
|
* [Known users](#known-users)
|
||||||
@@ -68,6 +70,26 @@ handles your haskell packages and can demand that [a specific version](https://c
|
|||||||
For man pages to work you need [man-db](http://man-db.nongnu.org/) as your `man` provider, then issue `man ghc`. Manpages only work for the currently set ghc.
|
For man pages to work you need [man-db](http://man-db.nongnu.org/) as your `man` provider, then issue `man ghc`. Manpages only work for the currently set ghc.
|
||||||
`MANPATH` may be required to be unset.
|
`MANPATH` may be required to be unset.
|
||||||
|
|
||||||
|
### Shell-completion
|
||||||
|
|
||||||
|
Shell completions are in `shell-completions`.
|
||||||
|
|
||||||
|
For bash: install `shell-completions/bash`
|
||||||
|
as e.g. `/etc/bash_completion.d/ghcup` (depending on distro)
|
||||||
|
and make sure your bashrc sources the startup script
|
||||||
|
(`/usr/share/bash-completion/bash_completion` on some distros).
|
||||||
|
|
||||||
|
### Cross support
|
||||||
|
|
||||||
|
ghcup can compile and install a cross GHC for any target. However, this
|
||||||
|
requires that the build host has a complete cross toolchain and various
|
||||||
|
libraries installed for the target platform.
|
||||||
|
|
||||||
|
Consult the GHC documentation on the [prerequisites](https://gitlab.haskell.org/ghc/ghc/-/wikis/building/cross-compiling#tools-to-install).
|
||||||
|
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.
|
||||||
|
|
||||||
## Design goals
|
## Design goals
|
||||||
|
|
||||||
1. simplicity
|
1. simplicity
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
|
|
||||||
2. Add/fix downloads to `GHCupDownloads` module, then run `ghcup-gen gen` to generate the new json and validate it via `ghcup-gen check`.
|
2. Add/fix downloads to `GHCupDownloads` module, then run `ghcup-gen gen` to generate the new json and validate it via `ghcup-gen check`.
|
||||||
|
|
||||||
3. Commit and git push with tag. Wait for tests to succeed.
|
3. Commit and git push with tag. Wait for tests to succeed and release artifacts to build.
|
||||||
|
|
||||||
4. Upload the new `ghcup-<ver>.json` to `webhost.haskell.org/ghcup/data/`.
|
4. Download release artifacts and upload them `downloads.haskell.org/ghcup`
|
||||||
|
|
||||||
|
5. Add release artifacts to GHCupDownloads (see point 2.)
|
||||||
|
|
||||||
|
6. Upload the final `ghcup-<ver>.json` to `webhost.haskell.org/ghcup/data/`.
|
||||||
|
|
||||||
5. Build ghcup releases for Linux (fully static), mac (with `-fcurl`) and FreeBSD (with `-fcurl`). Upload to `webhost.haskell.org/ghcup/bin/` and update symlinks.
|
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
|
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import GHCup.Data.GHCupInfo
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
import GHCupInfo
|
|
||||||
|
|
||||||
import Data.Aeson ( eitherDecode, encode )
|
import Data.Aeson ( eitherDecode, encode )
|
||||||
import Data.Aeson.Encode.Pretty
|
import Data.Aeson.Encode.Pretty
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ import Haskus.Utils.Variant.Excepts
|
|||||||
import Optics
|
import Optics
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import System.IO
|
import System.IO
|
||||||
|
import Text.ParserCombinators.ReadP
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Version as V
|
||||||
|
|
||||||
|
|
||||||
data ValidationError = InternalError String
|
data ValidationError = InternalError String
|
||||||
@@ -61,8 +64,9 @@ validate dls = do
|
|||||||
forM_ (M.toList $ _viArch vi) $ \(arch, pspecs) -> do
|
forM_ (M.toList $ _viArch vi) $ \(arch, pspecs) -> do
|
||||||
checkHasRequiredPlatforms t v arch (M.keys pspecs)
|
checkHasRequiredPlatforms t v arch (M.keys pspecs)
|
||||||
|
|
||||||
checkGHCisSemver
|
checkGHCVerIsValid
|
||||||
forM_ (M.toList dls) $ \(t, _) -> checkMandatoryTags t
|
forM_ (M.toList dls) $ \(t, _) -> checkMandatoryTags t
|
||||||
|
_ <- checkGHCHasBaseVersion
|
||||||
|
|
||||||
-- exit
|
-- exit
|
||||||
e <- liftIO $ readIORef ref
|
e <- liftIO $ readIORef ref
|
||||||
@@ -105,16 +109,19 @@ validate dls = do
|
|||||||
lift $ $(logError) [i|Tags not unique for #{tool}: #{xs}|]
|
lift $ $(logError) [i|Tags not unique for #{tool}: #{xs}|]
|
||||||
addError
|
addError
|
||||||
where
|
where
|
||||||
isUniqueTag Latest = True
|
isUniqueTag Latest = True
|
||||||
isUniqueTag Recommended = True
|
isUniqueTag Recommended = True
|
||||||
|
isUniqueTag (Base _) = False
|
||||||
|
isUniqueTag (UnknownTag _) = False
|
||||||
|
|
||||||
checkGHCisSemver = do
|
checkGHCVerIsValid = do
|
||||||
let ghcVers = toListOf (ix GHC % to M.keys % folded) dls
|
let ghcVers = toListOf (ix GHC % to M.keys % folded) dls
|
||||||
forM_ ghcVers $ \v -> case semver (prettyVer v) of
|
forM_ ghcVers $ \v ->
|
||||||
Left _ -> do
|
case [ x | (x,"") <- readP_to_S V.parseVersion (T.unpack . prettyVer $ v) ] of
|
||||||
lift $ $(logError) [i|GHC version #{v} is not valid semver|]
|
[_] -> pure ()
|
||||||
addError
|
_ -> do
|
||||||
Right _ -> pure ()
|
lift $ $(logError) [i|GHC version #{v} is not valid |]
|
||||||
|
addError
|
||||||
|
|
||||||
-- a tool must have at least one of each mandatory tags
|
-- a tool must have at least one of each mandatory tags
|
||||||
checkMandatoryTags tool = do
|
checkMandatoryTags tool = do
|
||||||
@@ -125,6 +132,17 @@ validate dls = do
|
|||||||
addError
|
addError
|
||||||
True -> pure ()
|
True -> pure ()
|
||||||
|
|
||||||
|
-- all GHC versions must have a base tag
|
||||||
|
checkGHCHasBaseVersion = do
|
||||||
|
let allTags = M.toList $ availableToolVersions dls GHC
|
||||||
|
forM allTags $ \(ver, tags) -> case any isBase tags of
|
||||||
|
False -> do
|
||||||
|
lift $ $(logError) [i|Base tag missing from GHC ver #{ver}|]
|
||||||
|
addError
|
||||||
|
True -> pure ()
|
||||||
|
|
||||||
|
isBase (Base _) = True
|
||||||
|
isBase _ = False
|
||||||
|
|
||||||
validateTarballs :: ( Monad m
|
validateTarballs :: ( Monad m
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
@@ -161,7 +179,7 @@ validateTarballs dls = do
|
|||||||
|
|
||||||
where
|
where
|
||||||
downloadAll dli = do
|
downloadAll dli = do
|
||||||
let settings = Settings True False
|
let settings = Settings True False Never Curl
|
||||||
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
||||||
, colorOutter = B.hPut stderr
|
, colorOutter = B.hPut stderr
|
||||||
, rawOutter = (\_ -> pure ())
|
, rawOutter = (\_ -> pure ())
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import GHCup.Types
|
|||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
|
import GHCup.Utils.MegaParsec
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Version
|
import GHCup.Version
|
||||||
|
|
||||||
@@ -32,11 +33,12 @@ import Data.Bifunctor
|
|||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.Either
|
import Data.Either
|
||||||
import Data.Functor
|
import Data.Functor
|
||||||
import Data.List ( intercalate )
|
import Data.List ( intercalate, sort )
|
||||||
|
import Data.List.NonEmpty (NonEmpty ((:|)))
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions hiding ( str )
|
||||||
import Data.Void
|
import Data.Void
|
||||||
import GHC.IO.Encoding
|
import GHC.IO.Encoding
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
@@ -73,6 +75,8 @@ data Options = Options
|
|||||||
, optCache :: Bool
|
, optCache :: Bool
|
||||||
, optUrlSource :: Maybe URI
|
, optUrlSource :: Maybe URI
|
||||||
, optNoVerify :: Bool
|
, optNoVerify :: Bool
|
||||||
|
, optKeepDirs :: KeepDirs
|
||||||
|
, optsDownloader :: Downloader
|
||||||
-- commands
|
-- commands
|
||||||
, optCommand :: Command
|
, optCommand :: Command
|
||||||
}
|
}
|
||||||
@@ -89,9 +93,13 @@ data Command
|
|||||||
| ToolRequirements
|
| ToolRequirements
|
||||||
| ChangeLog ChangeLogOptions
|
| ChangeLog ChangeLogOptions
|
||||||
|
|
||||||
data ToolVersion = ToolVersion Version
|
data ToolVersion = ToolVersion GHCTargetVersion -- target is ignored for cabal
|
||||||
| ToolTag Tag
|
| ToolTag Tag
|
||||||
|
|
||||||
|
prettyToolVer :: ToolVersion -> String
|
||||||
|
prettyToolVer (ToolVersion v') = T.unpack $ prettyTVer v'
|
||||||
|
prettyToolVer (ToolTag t) = show t
|
||||||
|
|
||||||
|
|
||||||
data InstallOptions = InstallOptions
|
data InstallOptions = InstallOptions
|
||||||
{ instVer :: Maybe ToolVersion
|
{ instVer :: Maybe ToolVersion
|
||||||
@@ -103,20 +111,31 @@ data SetGHCOptions = SetGHCOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
data ListOptions = ListOptions
|
data ListOptions = ListOptions
|
||||||
{ lTool :: Maybe Tool
|
{ lTool :: Maybe Tool
|
||||||
, lCriteria :: Maybe ListCriteria
|
, lCriteria :: Maybe ListCriteria
|
||||||
|
, lRawFormat :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data RmOptions = RmOptions
|
data RmOptions = RmOptions
|
||||||
{ ghcVer :: Version
|
{ ghcVer :: GHCTargetVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data CompileCommand = CompileGHC CompileOptions
|
data CompileCommand = CompileGHC GHCCompileOptions
|
||||||
| CompileCabal CompileOptions
|
| CompileCabal CabalCompileOptions
|
||||||
|
|
||||||
|
|
||||||
data CompileOptions = CompileOptions
|
data GHCCompileOptions = GHCCompileOptions
|
||||||
|
{ targetVer :: Version
|
||||||
|
, bootstrapGhc :: Either Version (Path Abs)
|
||||||
|
, jobs :: Maybe Int
|
||||||
|
, buildConfig :: Maybe (Path Abs)
|
||||||
|
, patchDir :: Maybe (Path Abs)
|
||||||
|
, crossTarget :: Maybe Text
|
||||||
|
, addConfArgs :: [Text]
|
||||||
|
}
|
||||||
|
|
||||||
|
data CabalCompileOptions = CabalCompileOptions
|
||||||
{ targetVer :: Version
|
{ targetVer :: Version
|
||||||
, bootstrapGhc :: Either Version (Path Abs)
|
, bootstrapGhc :: Either Version (Path Abs)
|
||||||
, jobs :: Maybe Int
|
, jobs :: Maybe Int
|
||||||
@@ -158,6 +177,31 @@ opts =
|
|||||||
(short 'n' <> long "no-verify" <> help
|
(short 'n' <> long "no-verify" <> help
|
||||||
"Skip tarball checksum verification"
|
"Skip tarball checksum verification"
|
||||||
)
|
)
|
||||||
|
<*> option
|
||||||
|
(eitherReader keepOnParser)
|
||||||
|
( long "keep"
|
||||||
|
<> metavar "<always|errors|never>"
|
||||||
|
<> help
|
||||||
|
"Keep build directories? (default: never)"
|
||||||
|
<> value Never
|
||||||
|
<> hidden
|
||||||
|
)
|
||||||
|
<*> option
|
||||||
|
(eitherReader downloaderParser)
|
||||||
|
( long "downloader"
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
<> metavar "<internal|curl|wget>"
|
||||||
|
<> help
|
||||||
|
"Downloader to use (default: internal)"
|
||||||
|
<> value Internal
|
||||||
|
#else
|
||||||
|
<> metavar "<curl|wget>"
|
||||||
|
<> help
|
||||||
|
"Downloader to use (default: curl)"
|
||||||
|
<> value Curl
|
||||||
|
#endif
|
||||||
|
<> hidden
|
||||||
|
)
|
||||||
<*> com
|
<*> com
|
||||||
where
|
where
|
||||||
parseUri s' =
|
parseUri s' =
|
||||||
@@ -309,6 +353,9 @@ listOpts =
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
<*> switch
|
||||||
|
(short 'r' <> long "raw-format" <> help "More machine-parsable format"
|
||||||
|
)
|
||||||
|
|
||||||
rmOpts :: Parser RmOptions
|
rmOpts :: Parser RmOptions
|
||||||
rmOpts = RmOptions <$> versionArgument
|
rmOpts = RmOptions <$> versionArgument
|
||||||
@@ -341,7 +388,7 @@ compileP = subparser
|
|||||||
"ghc"
|
"ghc"
|
||||||
( CompileGHC
|
( CompileGHC
|
||||||
<$> (info
|
<$> (info
|
||||||
(compileOpts <**> helper)
|
(ghcCompileOpts <**> helper)
|
||||||
( progDesc "Compile GHC from source"
|
( progDesc "Compile GHC from source"
|
||||||
<> footerDoc (Just $ text compileFooter)
|
<> footerDoc (Just $ text compileFooter)
|
||||||
)
|
)
|
||||||
@@ -351,7 +398,7 @@ compileP = subparser
|
|||||||
"cabal"
|
"cabal"
|
||||||
( CompileCabal
|
( CompileCabal
|
||||||
<$> (info
|
<$> (info
|
||||||
(compileOpts <**> helper)
|
(cabalCompileOpts <**> helper)
|
||||||
( progDesc "Compile Cabal from source"
|
( progDesc "Compile Cabal from source"
|
||||||
<> footerDoc (Just $ text compileCabalFooter)
|
<> footerDoc (Just $ text compileCabalFooter)
|
||||||
)
|
)
|
||||||
@@ -364,9 +411,19 @@ compileP = subparser
|
|||||||
a self-contained "~/.ghcup/ghc/<ghcver>" directory
|
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>".
|
||||||
|
|
||||||
|
This also allows building a cross-compiler. Consult the documentation
|
||||||
|
first: <https://gitlab.haskell.org/ghc/ghc/-/wikis/building/cross-compiling#configuring-the-build>
|
||||||
|
|
||||||
|
ENV variables:
|
||||||
|
Various toolchain variables will be passed onto the ghc build system,
|
||||||
|
such as: CC, LD, OBJDUMP, NM, AR, RANLIB.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
ghcup compile ghc -j 4 -v 8.4.2 -b 8.2.2
|
ghcup compile ghc -j 4 -v 8.4.2 -b 8.2.2
|
||||||
ghcup compile ghc -j 4 -v 8.4.2 -b /usr/bin/ghc-8.2.2|]
|
# specify path to bootstrap ghc
|
||||||
|
ghcup compile ghc -j 4 -v 8.4.2 -b /usr/bin/ghc-8.2.2
|
||||||
|
# build cross compiler
|
||||||
|
ghcup compile ghc -j 4 -v 8.4.2 -b 8.2.2 -x armv7-unknown-linux-gnueabihf --config $(pwd)/build.mk -- --enable-unregisterised|]
|
||||||
compileCabalFooter = [i|Discussion:
|
compileCabalFooter = [i|Discussion:
|
||||||
Compiles and installs the specified Cabal version
|
Compiles and installs the specified Cabal version
|
||||||
into "~/.ghcup/bin".
|
into "~/.ghcup/bin".
|
||||||
@@ -376,10 +433,24 @@ Examples:
|
|||||||
ghcup compile cabal -j 4 -v 3.2.0.0 -b /usr/bin/ghc-8.6.5|]
|
ghcup compile cabal -j 4 -v 3.2.0.0 -b /usr/bin/ghc-8.6.5|]
|
||||||
|
|
||||||
|
|
||||||
|
ghcCompileOpts :: Parser GHCCompileOptions
|
||||||
|
ghcCompileOpts =
|
||||||
|
(\CabalCompileOptions {..} crossTarget addConfArgs -> GHCCompileOptions { .. }
|
||||||
|
)
|
||||||
|
<$> cabalCompileOpts
|
||||||
|
<*> (optional
|
||||||
|
(option
|
||||||
|
str
|
||||||
|
(short 'x' <> long "cross-target" <> metavar "CROSS_TARGET" <> help
|
||||||
|
"Build cross-compiler for this platform"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
<*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to configure, prefix with '-- ' (longopts)"))
|
||||||
|
|
||||||
compileOpts :: Parser CompileOptions
|
cabalCompileOpts :: Parser CabalCompileOptions
|
||||||
compileOpts =
|
cabalCompileOpts =
|
||||||
CompileOptions
|
CabalCompileOptions
|
||||||
<$> (option
|
<$> (option
|
||||||
(eitherReader
|
(eitherReader
|
||||||
(bimap (const "Not a valid version") id . version . T.pack)
|
(bimap (const "Not a valid version") id . version . T.pack)
|
||||||
@@ -454,12 +525,12 @@ toolVersionArgument =
|
|||||||
argument (eitherReader toolVersionEither) (metavar "VERSION|TAG")
|
argument (eitherReader toolVersionEither) (metavar "VERSION|TAG")
|
||||||
|
|
||||||
|
|
||||||
versionArgument :: Parser Version
|
versionArgument :: Parser GHCTargetVersion
|
||||||
versionArgument = argument (eitherReader versionEither) (metavar "VERSION")
|
versionArgument = argument (eitherReader tVersionEither) (metavar "VERSION")
|
||||||
|
|
||||||
versionParser :: Parser Version
|
versionParser :: Parser GHCTargetVersion
|
||||||
versionParser = option
|
versionParser = option
|
||||||
(eitherReader versionEither)
|
(eitherReader tVersionEither)
|
||||||
(short 'v' <> long "version" <> metavar "VERSION" <> help "The target version"
|
(short 'v' <> long "version" <> metavar "VERSION" <> help "The target version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -467,18 +538,20 @@ tagEither :: String -> Either String Tag
|
|||||||
tagEither s' = case fmap toLower s' of
|
tagEither s' = case fmap toLower s' of
|
||||||
"recommended" -> Right Recommended
|
"recommended" -> Right Recommended
|
||||||
"latest" -> Right Latest
|
"latest" -> Right Latest
|
||||||
|
('b':'a':'s':'e':'-':ver') -> case pvp (T.pack ver') of
|
||||||
|
Right x -> Right (Base x)
|
||||||
|
Left _ -> Left [i|Invalid PVP version for base #{ver'}|]
|
||||||
other -> Left ([i|Unknown tag #{other}|])
|
other -> Left ([i|Unknown tag #{other}|])
|
||||||
|
|
||||||
versionEither :: String -> Either String Version
|
|
||||||
versionEither s' =
|
tVersionEither :: String -> Either String GHCTargetVersion
|
||||||
-- 'version' is a bit too lax and will parse typoed tags
|
tVersionEither =
|
||||||
case readMaybe ((: []) . head $ s') :: Maybe Int of
|
bimap (const "Not a valid version") id . MP.parse ghcTargetVerP "" . T.pack
|
||||||
Just _ -> bimap (const "Not a valid version") id . version . T.pack $ s'
|
|
||||||
Nothing -> Left "Not a valid version"
|
|
||||||
|
|
||||||
toolVersionEither :: String -> Either String ToolVersion
|
toolVersionEither :: String -> Either String ToolVersion
|
||||||
toolVersionEither s' =
|
toolVersionEither s' =
|
||||||
bimap id ToolTag (tagEither s') <|> bimap id ToolVersion (versionEither s')
|
bimap id ToolTag (tagEither s') <|> bimap id ToolVersion (tVersionEither s')
|
||||||
|
|
||||||
|
|
||||||
toolParser :: String -> Either String Tool
|
toolParser :: String -> Either String Tool
|
||||||
@@ -495,6 +568,24 @@ criteriaParser s' | t == T.pack "installed" = Right ListInstalled
|
|||||||
where t = T.toLower (T.pack s')
|
where t = T.toLower (T.pack s')
|
||||||
|
|
||||||
|
|
||||||
|
keepOnParser :: String -> Either String KeepDirs
|
||||||
|
keepOnParser s' | t == T.pack "always" = Right Always
|
||||||
|
| t == T.pack "errors" = Right Errors
|
||||||
|
| t == T.pack "never" = Right Never
|
||||||
|
| otherwise = Left ("Unknown keep value: " <> s')
|
||||||
|
where t = T.toLower (T.pack s')
|
||||||
|
|
||||||
|
|
||||||
|
downloaderParser :: String -> Either String Downloader
|
||||||
|
downloaderParser s' | t == T.pack "curl" = Right Curl
|
||||||
|
| t == T.pack "wget" = Right Wget
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
| t == T.pack "internal" = Right Internal
|
||||||
|
#endif
|
||||||
|
| otherwise = Left ("Unknown downloader value: " <> s')
|
||||||
|
where t = T.toLower (T.pack s')
|
||||||
|
|
||||||
|
|
||||||
platformParser :: String -> Either String PlatformRequest
|
platformParser :: String -> Either String PlatformRequest
|
||||||
platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
|
platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
|
||||||
Right r -> pure r
|
Right r -> pure r
|
||||||
@@ -554,24 +645,15 @@ platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
|
|||||||
MP.setInput rest
|
MP.setInput rest
|
||||||
pure v
|
pure v
|
||||||
|
|
||||||
choice' [] = fail "Empty list"
|
|
||||||
choice' [x ] = x
|
|
||||||
choice' (x : xs) = MP.try x <|> choice' xs
|
|
||||||
|
|
||||||
parseUntil :: MP.Parsec Void Text Text -> MP.Parsec Void Text Text
|
|
||||||
parseUntil p = do
|
|
||||||
(MP.try (MP.lookAhead p) $> mempty)
|
|
||||||
<|> (do
|
|
||||||
c <- T.singleton <$> MP.anySingle
|
|
||||||
c2 <- parseUntil p
|
|
||||||
pure (c `mappend` c2)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
toSettings :: Options -> Settings
|
toSettings :: Options -> Settings
|
||||||
toSettings Options {..} =
|
toSettings Options {..} =
|
||||||
let cache = optCache
|
let cache = optCache
|
||||||
noVerify = optNoVerify
|
noVerify = optNoVerify
|
||||||
|
keepDirs = optKeepDirs
|
||||||
|
downloader = optsDownloader
|
||||||
in Settings { .. }
|
in Settings { .. }
|
||||||
|
|
||||||
|
|
||||||
@@ -624,21 +706,31 @@ main = do
|
|||||||
<> help "Show the numeric version (for use in scripts)"
|
<> help "Show the numeric version (for use in scripts)"
|
||||||
<> hidden
|
<> hidden
|
||||||
)
|
)
|
||||||
|
let listCommands = infoOption
|
||||||
|
"install set rm install-cabal list upgrade compile debug-info tool-requirements changelog"
|
||||||
|
( long "list-commands"
|
||||||
|
<> help "List available commands for shell completion"
|
||||||
|
<> internal
|
||||||
|
)
|
||||||
|
|
||||||
let main_footer = [i|Discussion:
|
let main_footer = [i|Discussion:
|
||||||
ghcup installs the Glasgow Haskell Compiler from the official
|
ghcup installs the Glasgow Haskell Compiler from the official
|
||||||
release channels, enabling you to easily switch between different
|
release channels, enabling you to easily switch between different
|
||||||
versions.
|
versions. It maintains a self-contained ~/.ghcup directory.
|
||||||
|
|
||||||
|
ENV variables:
|
||||||
|
* TMPDIR: where ghcup does the work (unpacking, building, ...)
|
||||||
|
* GHCUP_INSTALL_BASE_PREFIX: the base of ghcup (default: $HOME)
|
||||||
|
|
||||||
Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
||||||
|
|
||||||
customExecParser
|
customExecParser
|
||||||
(prefs showHelpOnError)
|
(prefs showHelpOnError)
|
||||||
(info (opts <**> helper <**> versionHelp <**> numericVersionHelp)
|
(info (opts <**> helper <**> versionHelp <**> numericVersionHelp <**> listCommands)
|
||||||
(footerDoc (Just $ text main_footer))
|
(footerDoc (Just $ text main_footer))
|
||||||
)
|
)
|
||||||
>>= \opt@Options {..} -> do
|
>>= \opt@Options {..} -> do
|
||||||
let settings = toSettings opt
|
let settings@Settings{..} = toSettings opt
|
||||||
|
|
||||||
-- create ~/.ghcup dir
|
-- create ~/.ghcup dir
|
||||||
ghcdir <- ghcupBaseDir
|
ghcdir <- ghcupBaseDir
|
||||||
@@ -749,9 +841,9 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
|||||||
(GHCupInfo treq dls) <-
|
(GHCupInfo treq dls) <-
|
||||||
( runLogger
|
( runLogger
|
||||||
. flip runReaderT settings
|
. flip runReaderT settings
|
||||||
. runE @'[JSONError , DownloadFailed]
|
. runE @'[JSONError , DownloadFailed, FileDoesNotExistError]
|
||||||
$ liftE
|
$ liftE
|
||||||
$ getDownloads (maybe GHCupURL OwnSource optUrlSource)
|
$ getDownloadsF (maybe GHCupURL OwnSource optUrlSource)
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight r -> pure r
|
VRight r -> pure r
|
||||||
@@ -765,7 +857,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
|||||||
Install (InstallOptions {..}) ->
|
Install (InstallOptions {..}) ->
|
||||||
(runInstTool $ do
|
(runInstTool $ do
|
||||||
v <- liftE $ fromVersion dls instVer GHC
|
v <- liftE $ fromVersion dls instVer GHC
|
||||||
liftE $ installGHCBin dls v instPlatform
|
liftE $ installGHCBin dls (_tvVersion v) instPlatform -- FIXME: ugly sharing of tool version
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> do
|
VRight _ -> do
|
||||||
@@ -776,10 +868,18 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
|
|||||||
[i|GHC ver #{prettyVer v} already installed|]
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
VLeft (V (BuildFailed tmpdir e)) -> do
|
VLeft (V (BuildFailed tmpdir e)) -> do
|
||||||
runLogger
|
case keepDirs of
|
||||||
($(logError) [i|Build failed with #{e}
|
Never -> runLogger ($(logError) [i|Build failed with #{e}|])
|
||||||
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
_ -> runLogger ($(logError) [i|Build failed with #{e}
|
||||||
)
|
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||||
|
Make sure to clean up #{tmpdir} afterwards.|])
|
||||||
|
pure $ ExitFailure 3
|
||||||
|
VLeft (V NoDownload) -> do
|
||||||
|
|
||||||
|
runLogger $ do
|
||||||
|
case instVer of
|
||||||
|
Just iver -> $(logError) [i|No available GHC version for #{prettyToolVer iver}|]
|
||||||
|
Nothing -> $(logError) [i|No available recommended GHC version|]
|
||||||
pure $ ExitFailure 3
|
pure $ ExitFailure 3
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger $ do
|
runLogger $ do
|
||||||
@@ -789,7 +889,7 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
InstallCabal (InstallOptions {..}) ->
|
InstallCabal (InstallOptions {..}) ->
|
||||||
(runInstTool $ do
|
(runInstTool $ do
|
||||||
v <- liftE $ fromVersion dls instVer Cabal
|
v <- liftE $ fromVersion dls instVer Cabal
|
||||||
liftE $ installCabalBin dls v instPlatform
|
liftE $ installCabalBin dls (_tvVersion v) instPlatform -- FIXME: ugly sharing of tool version
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> do
|
VRight _ -> do
|
||||||
@@ -799,6 +899,13 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
runLogger $ $(logWarn)
|
runLogger $ $(logWarn)
|
||||||
[i|Cabal ver #{prettyVer v} already installed|]
|
[i|Cabal ver #{prettyVer v} already installed|]
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
|
VLeft (V NoDownload) -> do
|
||||||
|
|
||||||
|
runLogger $ do
|
||||||
|
case instVer of
|
||||||
|
Just iver -> $(logError) [i|No available Cabal version for #{prettyToolVer iver}|]
|
||||||
|
Nothing -> $(logError) [i|No available recommended Cabal version|]
|
||||||
|
pure $ ExitFailure 4
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger $ do
|
runLogger $ do
|
||||||
$(logError) [i|#{e}|]
|
$(logError) [i|#{e}|]
|
||||||
@@ -811,10 +918,10 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
liftE $ setGHC v SetGHCOnly
|
liftE $ setGHC v SetGHCOnly
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight v -> do
|
VRight (GHCTargetVersion{..}) -> do
|
||||||
runLogger
|
runLogger
|
||||||
$ $(logInfo)
|
$ $(logInfo)
|
||||||
[i|GHC #{prettyVer v} successfully set as default version|]
|
[i|GHC #{prettyVer _tvVersion} successfully set as default version#{maybe "" (" for cross target " <>) _tvTarget}|]
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger ($(logError) [i|#{e}|])
|
runLogger ($(logError) [i|#{e}|])
|
||||||
@@ -827,7 +934,7 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight r -> do
|
VRight r -> do
|
||||||
liftIO $ printListResult r
|
liftIO $ printListResult lRawFormat r
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger ($(logError) [i|#{e}|])
|
runLogger ($(logError) [i|#{e}|])
|
||||||
@@ -854,13 +961,14 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
runLogger ($(logError) [i|#{e}|])
|
runLogger ($(logError) [i|#{e}|])
|
||||||
pure $ ExitFailure 8
|
pure $ ExitFailure 8
|
||||||
|
|
||||||
Compile (CompileGHC CompileOptions {..}) ->
|
Compile (CompileGHC GHCCompileOptions {..}) ->
|
||||||
(runCompileGHC $ liftE $ compileGHC dls
|
(runCompileGHC $ liftE $ compileGHC dls
|
||||||
targetVer
|
(GHCTargetVersion crossTarget targetVer)
|
||||||
bootstrapGhc
|
bootstrapGhc
|
||||||
jobs
|
jobs
|
||||||
buildConfig
|
buildConfig
|
||||||
patchDir
|
patchDir
|
||||||
|
addConfArgs
|
||||||
)
|
)
|
||||||
>>= \case
|
>>= \case
|
||||||
VRight _ -> do
|
VRight _ -> do
|
||||||
@@ -872,17 +980,18 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
[i|GHC ver #{prettyVer v} already installed|]
|
[i|GHC ver #{prettyVer v} already installed|]
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
VLeft (V (BuildFailed tmpdir e)) -> do
|
VLeft (V (BuildFailed tmpdir e)) -> do
|
||||||
runLogger
|
case keepDirs of
|
||||||
($(logError) [i|Build failed with #{e}
|
Never -> runLogger ($(logError) [i|Build failed with #{e}
|
||||||
|
Check the logs at ~/.ghcup/logs|])
|
||||||
|
_ -> 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 ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||||
Make sure to clean up #{tmpdir} afterwards.|]
|
Make sure to clean up #{tmpdir} afterwards.|])
|
||||||
)
|
|
||||||
pure $ ExitFailure 9
|
pure $ ExitFailure 9
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger ($(logError) [i|#{e}|])
|
runLogger ($(logError) [i|#{e}|])
|
||||||
pure $ ExitFailure 9
|
pure $ ExitFailure 9
|
||||||
|
|
||||||
Compile (CompileCabal CompileOptions {..}) ->
|
Compile (CompileCabal CabalCompileOptions {..}) ->
|
||||||
(runCompileCabal $ do
|
(runCompileCabal $ do
|
||||||
liftE $ compileCabal dls targetVer bootstrapGhc jobs patchDir
|
liftE $ compileCabal dls targetVer bootstrapGhc jobs patchDir
|
||||||
)
|
)
|
||||||
@@ -894,10 +1003,11 @@ Make sure to clean up #{tmpdir} afterwards.|]
|
|||||||
)
|
)
|
||||||
pure ExitSuccess
|
pure ExitSuccess
|
||||||
VLeft (V (BuildFailed tmpdir e)) -> do
|
VLeft (V (BuildFailed tmpdir e)) -> do
|
||||||
runLogger
|
case keepDirs of
|
||||||
($(logError) [i|Build failed with #{e}
|
Never -> runLogger ($(logError) [i|Build failed with #{e}|])
|
||||||
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.|]
|
_ -> runLogger ($(logError) [i|Build failed with #{e}
|
||||||
)
|
Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues.
|
||||||
|
Make sure to clean up #{tmpdir} afterwards.|])
|
||||||
pure $ ExitFailure 10
|
pure $ ExitFailure 10
|
||||||
VLeft e -> do
|
VLeft e -> do
|
||||||
runLogger ($(logError) [i|#{e}|])
|
runLogger ($(logError) [i|#{e}|])
|
||||||
@@ -952,7 +1062,7 @@ Check the logs at ~/.ghcup/logs and the build directory #{tmpdir} for more clues
|
|||||||
ver' = maybe
|
ver' = maybe
|
||||||
(Right Latest)
|
(Right Latest)
|
||||||
(\case
|
(\case
|
||||||
ToolVersion tv -> Left tv
|
ToolVersion tv -> Left (_tvVersion tv) -- FIXME: ugly sharing of ToolVersion
|
||||||
ToolTag t -> Right t
|
ToolTag t -> Right t
|
||||||
)
|
)
|
||||||
clToolVer
|
clToolVer
|
||||||
@@ -989,47 +1099,73 @@ fromVersion :: Monad m
|
|||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe ToolVersion
|
-> Maybe ToolVersion
|
||||||
-> Tool
|
-> Tool
|
||||||
-> Excepts '[TagNotFound] m Version
|
-> Excepts '[TagNotFound] m GHCTargetVersion
|
||||||
fromVersion av Nothing tool =
|
fromVersion av Nothing tool =
|
||||||
getRecommended av tool ?? TagNotFound Recommended tool
|
mkTVer <$> getRecommended av tool ?? TagNotFound Recommended tool
|
||||||
fromVersion _ (Just (ToolVersion v)) _ = pure v
|
fromVersion av (Just (ToolVersion v)) _ = do
|
||||||
|
case pvp $ prettyVer (_tvVersion v) of
|
||||||
|
Left _ -> pure v
|
||||||
|
Right (PVP (major' :|[minor'])) ->
|
||||||
|
case getLatestGHCFor (fromIntegral major') (fromIntegral minor') av of
|
||||||
|
Just v' -> pure $ GHCTargetVersion (_tvTarget v) v'
|
||||||
|
Nothing -> pure v
|
||||||
|
Right _ -> pure v
|
||||||
fromVersion av (Just (ToolTag Latest)) tool =
|
fromVersion av (Just (ToolTag Latest)) tool =
|
||||||
getLatest av tool ?? TagNotFound Latest tool
|
mkTVer <$> getLatest av tool ?? TagNotFound Latest tool
|
||||||
fromVersion av (Just (ToolTag Recommended)) tool =
|
fromVersion av (Just (ToolTag Recommended)) tool =
|
||||||
getRecommended av tool ?? TagNotFound Recommended tool
|
mkTVer <$> getRecommended av tool ?? TagNotFound Recommended tool
|
||||||
|
fromVersion av (Just (ToolTag (Base pvp''))) GHC =
|
||||||
|
mkTVer <$> getLatestBaseVersion av pvp'' ?? TagNotFound (Base pvp'') GHC
|
||||||
|
fromVersion _ (Just (ToolTag t')) tool =
|
||||||
|
throwE $ TagNotFound t' tool
|
||||||
|
|
||||||
|
|
||||||
printListResult :: [ListResult] -> IO ()
|
printListResult :: Bool -> [ListResult] -> IO ()
|
||||||
printListResult lr = do
|
printListResult raw lr = do
|
||||||
-- https://gitlab.haskell.org/ghc/ghc/issues/8118
|
-- https://gitlab.haskell.org/ghc/ghc/issues/8118
|
||||||
setLocaleEncoding utf8
|
setLocaleEncoding utf8
|
||||||
|
|
||||||
let
|
let
|
||||||
formatted =
|
formatted =
|
||||||
gridString
|
gridString
|
||||||
[ column expand left def def
|
( (if raw then [] else [column expand left def def])
|
||||||
, column expand left def def
|
++ [ column expand left def def
|
||||||
, column expand left def def
|
, column expand left def def
|
||||||
, column expand left def def
|
, column expand left def def
|
||||||
, column expand left def def
|
, column expand left def def
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
. (\x -> if raw
|
||||||
|
then x
|
||||||
|
else [color Green "", "Tool", "Version", "Tags", "Notes"] : x
|
||||||
|
)
|
||||||
$ fmap
|
$ fmap
|
||||||
(\ListResult {..} ->
|
(\ListResult {..} ->
|
||||||
[ if
|
let marks = if
|
||||||
| lSet -> (color Green "✔✔")
|
| lSet -> (color Green "✔✔")
|
||||||
| lInstalled -> (color Green "✓")
|
| lInstalled -> (color Green "✓")
|
||||||
| otherwise -> (color Red "✗")
|
| otherwise -> (color Red "✗")
|
||||||
, fmap toLower . show $ lTool
|
in (if raw then [] else [marks])
|
||||||
, T.unpack . prettyVer $ lVer
|
++ [ fmap toLower . show $ lTool
|
||||||
, intercalate "," $ ((fmap . fmap) toLower . fmap show $ lTag)
|
, case lCross of
|
||||||
, intercalate "," $
|
Nothing -> T.unpack . prettyVer $ lVer
|
||||||
(if fromSrc then [color Blue "compiled"] else mempty)
|
Just c -> T.unpack (c <> "-" <> prettyVer lVer)
|
||||||
++ (if lStray then [color Blue "stray"] else mempty)
|
, intercalate "," $ (fmap printTag $ sort lTag)
|
||||||
]
|
, intercalate ","
|
||||||
|
$ (if fromSrc then [color' Blue "compiled"] else mempty)
|
||||||
|
++ (if lStray then [color' Blue "stray"] else mempty)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
lr
|
lr
|
||||||
putStrLn $ formatted
|
putStrLn $ formatted
|
||||||
|
where
|
||||||
|
printTag Recommended = color' Green "recommended"
|
||||||
|
printTag Latest = color' Yellow "latest"
|
||||||
|
printTag (Base pvp'') = "base-" ++ T.unpack (prettyPVP pvp'')
|
||||||
|
printTag (UnknownTag t ) = t
|
||||||
|
color' = case raw of
|
||||||
|
True -> flip const
|
||||||
|
False -> color
|
||||||
|
|
||||||
checkForUpdates :: (MonadThrow m, MonadIO m, MonadFail m, MonadLogger m)
|
checkForUpdates :: (MonadThrow m, MonadIO m, MonadFail m, MonadLogger m)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
|
|||||||
2278
ghcup-0.0.2.json
Normal file
2278
ghcup-0.0.2.json
Normal file
File diff suppressed because it is too large
Load Diff
27
ghcup.cabal
27
ghcup.cabal
@@ -1,6 +1,6 @@
|
|||||||
cabal-version: 3.0
|
cabal-version: 3.0
|
||||||
name: ghcup
|
name: ghcup
|
||||||
version: 0.1.4
|
version: 0.1.5
|
||||||
synopsis: ghc toolchain installer as an exe/library
|
synopsis: ghc toolchain installer as an exe/library
|
||||||
description:
|
description:
|
||||||
A rewrite of the shell script ghcup, for providing
|
A rewrite of the shell script ghcup, for providing
|
||||||
@@ -21,8 +21,8 @@ source-repository head
|
|||||||
type: git
|
type: git
|
||||||
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
||||||
|
|
||||||
flag Curl
|
flag internal-downloader
|
||||||
description: Use curl instead of http-io-streams for download
|
description: Compile the internal downloader, which links against OpenSSL
|
||||||
default: False
|
default: False
|
||||||
manual: True
|
manual: True
|
||||||
|
|
||||||
@@ -41,9 +41,6 @@ common ascii-string
|
|||||||
common async
|
common async
|
||||||
build-depends: async >=0.8
|
build-depends: async >=0.8
|
||||||
|
|
||||||
common attoparsec
|
|
||||||
build-depends: attoparsec >=0.13
|
|
||||||
|
|
||||||
common base
|
common base
|
||||||
build-depends: base >=4.12 && <5
|
build-depends: base >=4.12 && <5
|
||||||
|
|
||||||
@@ -230,7 +227,6 @@ library
|
|||||||
, aeson
|
, aeson
|
||||||
, ascii-string
|
, ascii-string
|
||||||
, async
|
, async
|
||||||
, attoparsec
|
|
||||||
, binary
|
, binary
|
||||||
, bytestring
|
, bytestring
|
||||||
, bz2
|
, bz2
|
||||||
@@ -248,6 +244,7 @@ library
|
|||||||
, hpath-posix
|
, hpath-posix
|
||||||
, language-bash
|
, language-bash
|
||||||
, lzma
|
, lzma
|
||||||
|
, megaparsec
|
||||||
, monad-logger
|
, monad-logger
|
||||||
, mtl
|
, mtl
|
||||||
, optics
|
, optics
|
||||||
@@ -279,6 +276,9 @@ library
|
|||||||
|
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
GHCup
|
GHCup
|
||||||
|
GHCup.Data.GHCupDownloads
|
||||||
|
GHCup.Data.GHCupInfo
|
||||||
|
GHCup.Data.ToolRequirements
|
||||||
GHCup.Download
|
GHCup.Download
|
||||||
GHCup.Download.Utils
|
GHCup.Download.Utils
|
||||||
GHCup.Errors
|
GHCup.Errors
|
||||||
@@ -292,6 +292,7 @@ library
|
|||||||
GHCup.Utils.Dirs
|
GHCup.Utils.Dirs
|
||||||
GHCup.Utils.File
|
GHCup.Utils.File
|
||||||
GHCup.Utils.Logger
|
GHCup.Utils.Logger
|
||||||
|
GHCup.Utils.MegaParsec
|
||||||
GHCup.Utils.Prelude
|
GHCup.Utils.Prelude
|
||||||
GHCup.Utils.String.QQ
|
GHCup.Utils.String.QQ
|
||||||
GHCup.Utils.Version.QQ
|
GHCup.Utils.Version.QQ
|
||||||
@@ -301,15 +302,14 @@ library
|
|||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
hs-source-dirs: lib
|
hs-source-dirs: lib
|
||||||
|
|
||||||
if !flag(curl)
|
if flag(internal-downloader)
|
||||||
import:
|
import:
|
||||||
, HsOpenSSL
|
, HsOpenSSL
|
||||||
, http-io-streams
|
, http-io-streams
|
||||||
, io-streams
|
, io-streams
|
||||||
, terminal-progress-bar
|
, terminal-progress-bar
|
||||||
exposed-modules: GHCup.Download.IOStreams
|
exposed-modules: GHCup.Download.IOStreams
|
||||||
else
|
cpp-options: -DINTERNAL_DOWNLOADER
|
||||||
cpp-options: -DCURL
|
|
||||||
|
|
||||||
executable ghcup
|
executable ghcup
|
||||||
import:
|
import:
|
||||||
@@ -344,6 +344,10 @@ executable ghcup
|
|||||||
hs-source-dirs: app/ghcup
|
hs-source-dirs: app/ghcup
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
if flag(internal-downloader)
|
||||||
|
cpp-options: -DINTERNAL_DOWNLOADER
|
||||||
|
|
||||||
|
|
||||||
executable ghcup-gen
|
executable ghcup-gen
|
||||||
import:
|
import:
|
||||||
config
|
config
|
||||||
@@ -372,9 +376,6 @@ executable ghcup-gen
|
|||||||
--
|
--
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
GHCupDownloads
|
|
||||||
GHCupInfo
|
|
||||||
ToolRequirements
|
|
||||||
Validate
|
Validate
|
||||||
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
|||||||
259
lib/GHCup.hs
259
lib/GHCup.hs
@@ -41,6 +41,7 @@ import Data.ByteString ( ByteString )
|
|||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
import Data.Word8
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
@@ -53,11 +54,14 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
|
import System.Posix.Env.ByteString ( getEnvironment )
|
||||||
import System.Posix.FilePath ( getSearchPath )
|
import System.Posix.FilePath ( getSearchPath )
|
||||||
import System.Posix.Files.ByteString
|
import System.Posix.Files.ByteString
|
||||||
|
|
||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
|
|
||||||
|
|
||||||
@@ -94,45 +98,34 @@ installGHCBin :: ( MonadFail m
|
|||||||
m
|
m
|
||||||
()
|
()
|
||||||
installGHCBin bDls ver mpfReq = do
|
installGHCBin bDls ver mpfReq = do
|
||||||
|
let tver = (mkTVer ver)
|
||||||
lift $ $(logDebug) [i|Requested to install GHC with #{ver}|]
|
lift $ $(logDebug) [i|Requested to install GHC with #{ver}|]
|
||||||
whenM (liftIO $ toolAlreadyInstalled GHC ver)
|
whenM (liftIO $ ghcInstalled tver)
|
||||||
$ (throwE $ AlreadyInstalled GHC ver)
|
$ (throwE $ AlreadyInstalled GHC ver)
|
||||||
Settings {..} <- lift ask
|
Settings {..} <- lift ask
|
||||||
pfreq@(PlatformRequest {..}) <- maybe (liftE $ platformRequest) pure mpfReq
|
pfreq@(PlatformRequest {..}) <- maybe (liftE $ platformRequest) pure mpfReq
|
||||||
|
|
||||||
-- download (or use cached version)
|
-- download (or use cached version)
|
||||||
dlinfo <- lE $ getDownloadInfo GHC ver pfreq bDls
|
dlinfo <- lE $ getDownloadInfo GHC ver pfreq bDls
|
||||||
dl <- liftE $ downloadCached dlinfo Nothing
|
dl <- liftE $ downloadCached dlinfo Nothing
|
||||||
|
|
||||||
-- unpack
|
-- unpack
|
||||||
tmpUnpack <- lift mkGhcupTmpDir
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
||||||
|
|
||||||
-- prepare paths
|
-- prepare paths
|
||||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
ghcdir <- liftIO $ ghcupGHCDir tver
|
||||||
|
|
||||||
-- the subdir of the archive where we do the work
|
-- the subdir of the archive where we do the work
|
||||||
let workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
let workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
-- Be careful about cleanup. We must catch both pure exceptions
|
liftE $ runBuildAction tmpUnpack (Just ghcdir) (installGHC' workdir ghcdir)
|
||||||
-- as well as async ones.
|
|
||||||
flip onException
|
|
||||||
(liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
|
||||||
$ catchAllE
|
|
||||||
(\es -> do
|
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
|
||||||
>> throwE (BuildFailed workdir es)
|
|
||||||
)
|
|
||||||
$ installGHC' workdir ghcdir
|
|
||||||
|
|
||||||
-- only clean up dir if the build succeeded
|
liftE $ postGHCInstall tver
|
||||||
liftIO $ deleteDirRecursive tmpUnpack
|
|
||||||
|
|
||||||
liftE $ postGHCInstall ver
|
|
||||||
|
|
||||||
where
|
where
|
||||||
-- | Install an unpacked GHC distribution. This only deals with the GHC build system and nothing else.
|
-- | Install an unpacked GHC distribution. This only deals with the GHC build system and nothing else.
|
||||||
installGHC' :: (MonadLogger m, MonadIO m)
|
installGHC' :: (MonadLogger m, MonadIO m)
|
||||||
=> Path Abs -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
=> Path Abs -- ^ Path to the unpacked GHC bindist (where the configure script resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> Path Abs -- ^ Path to install to
|
||||||
@@ -173,15 +166,15 @@ installCabalBin :: ( MonadMask m
|
|||||||
()
|
()
|
||||||
installCabalBin bDls ver mpfReq = do
|
installCabalBin bDls ver mpfReq = do
|
||||||
lift $ $(logDebug) [i|Requested to install cabal version #{ver}|]
|
lift $ $(logDebug) [i|Requested to install cabal version #{ver}|]
|
||||||
Settings {..} <- lift ask
|
Settings {..} <- lift ask
|
||||||
pfreq@(PlatformRequest {..}) <- maybe (liftE $ platformRequest) pure mpfReq
|
pfreq@(PlatformRequest {..}) <- maybe (liftE $ platformRequest) pure mpfReq
|
||||||
|
|
||||||
-- download (or use cached version)
|
-- download (or use cached version)
|
||||||
dlinfo <- lE $ getDownloadInfo Cabal ver pfreq bDls
|
dlinfo <- lE $ getDownloadInfo Cabal ver pfreq bDls
|
||||||
dl <- liftE $ downloadCached dlinfo Nothing
|
dl <- liftE $ downloadCached dlinfo Nothing
|
||||||
|
|
||||||
-- unpack
|
-- unpack
|
||||||
tmpUnpack <- lift withGHCupTmpDir
|
tmpUnpack <- lift withGHCupTmpDir
|
||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
||||||
|
|
||||||
@@ -195,7 +188,7 @@ installCabalBin bDls ver mpfReq = do
|
|||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
-- | Install an unpacked cabal distribution.
|
-- | Install an unpacked cabal distribution.
|
||||||
installCabal' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
installCabal' :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Path Abs -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
=> Path Abs -- ^ Path to the unpacked cabal bindist (where the executable resides)
|
||||||
-> Path Abs -- ^ Path to install to
|
-> Path Abs -- ^ Path to install to
|
||||||
@@ -227,11 +220,11 @@ installCabalBin bDls ver mpfReq = do
|
|||||||
-- Additionally creates a ~/.ghcup/share -> ~/.ghcup/ghc/<ver>/share symlink
|
-- Additionally creates a ~/.ghcup/share -> ~/.ghcup/ghc/<ver>/share symlink
|
||||||
-- for `SetGHCOnly` constructor.
|
-- for `SetGHCOnly` constructor.
|
||||||
setGHC :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
setGHC :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> GHCTargetVersion
|
||||||
-> SetGHC
|
-> SetGHC
|
||||||
-> Excepts '[NotInstalled] m Version
|
-> Excepts '[NotInstalled] m GHCTargetVersion
|
||||||
setGHC ver sghc = do
|
setGHC ver sghc = do
|
||||||
let verBS = verToBS ver
|
let verBS = verToBS (_tvVersion ver)
|
||||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||||
|
|
||||||
-- symlink destination
|
-- symlink destination
|
||||||
@@ -241,7 +234,7 @@ setGHC ver sghc = do
|
|||||||
-- first delete the old symlinks (this fixes compatibility issues
|
-- first delete the old symlinks (this fixes compatibility issues
|
||||||
-- with old ghcup)
|
-- with old ghcup)
|
||||||
case sghc of
|
case sghc of
|
||||||
SetGHCOnly -> liftE $ rmPlain ver
|
SetGHCOnly -> liftE $ rmPlain (_tvTarget ver)
|
||||||
SetGHC_XY -> lift $ rmMajorSymlinks ver
|
SetGHC_XY -> lift $ rmMajorSymlinks ver
|
||||||
SetGHC_XYZ -> lift $ rmMinorSymlinks ver
|
SetGHC_XYZ -> lift $ rmMinorSymlinks ver
|
||||||
|
|
||||||
@@ -251,9 +244,8 @@ setGHC ver sghc = do
|
|||||||
targetFile <- case sghc of
|
targetFile <- case sghc of
|
||||||
SetGHCOnly -> pure file
|
SetGHCOnly -> pure file
|
||||||
SetGHC_XY -> do
|
SetGHC_XY -> do
|
||||||
major' <-
|
major' <- (\(mj, mi) -> E.encodeUtf8 $ intToText mj <> "." <> intToText mi)
|
||||||
(\(mj, mi) -> E.encodeUtf8 $ intToText mj <> "." <> intToText mi)
|
<$> getMajorMinorV (_tvVersion ver)
|
||||||
<$> getGHCMajor ver
|
|
||||||
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
parseRel (toFilePath file <> B.singleton _hyphen <> major')
|
||||||
SetGHC_XYZ -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
SetGHC_XYZ -> parseRel (toFilePath file <> B.singleton _hyphen <> verBS)
|
||||||
|
|
||||||
@@ -264,7 +256,7 @@ setGHC ver sghc = do
|
|||||||
liftIO $ createSymlink fullF destL
|
liftIO $ createSymlink fullF destL
|
||||||
|
|
||||||
-- create symlink for share dir
|
-- create symlink for share dir
|
||||||
lift $ symlinkShareDir ghcdir verBS
|
when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir ghcdir verBS
|
||||||
|
|
||||||
pure ver
|
pure ver
|
||||||
|
|
||||||
@@ -304,6 +296,7 @@ data ListCriteria = ListInstalled
|
|||||||
data ListResult = ListResult
|
data ListResult = ListResult
|
||||||
{ lTool :: Tool
|
{ lTool :: Tool
|
||||||
, lVer :: Version
|
, lVer :: Version
|
||||||
|
, lCross :: Maybe Text -- ^ currently only for GHC
|
||||||
, lTag :: [Tag]
|
, lTag :: [Tag]
|
||||||
, lInstalled :: Bool
|
, lInstalled :: Bool
|
||||||
, lSet :: Bool -- ^ currently active version
|
, lSet :: Bool -- ^ currently active version
|
||||||
@@ -321,7 +314,7 @@ availableToolVersions av tool = view
|
|||||||
|
|
||||||
-- | List all versions from the download info, as well as stray
|
-- | List all versions from the download info, as well as stray
|
||||||
-- versions.
|
-- versions.
|
||||||
listVersions :: (MonadLogger m, MonadIO m)
|
listVersions :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe Tool
|
-> Maybe Tool
|
||||||
-> Maybe ListCriteria
|
-> Maybe ListCriteria
|
||||||
@@ -345,44 +338,58 @@ listVersions av lt criteria = case lt of
|
|||||||
pure (ghcvers <> cabalvers <> ghcupvers)
|
pure (ghcvers <> cabalvers <> ghcupvers)
|
||||||
|
|
||||||
where
|
where
|
||||||
strayGHCs :: (MonadLogger m, MonadIO m)
|
strayGHCs :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
=> Map.Map Version [Tag]
|
=> Map.Map Version [Tag]
|
||||||
-> m [ListResult]
|
-> m [ListResult]
|
||||||
strayGHCs avTools = do
|
strayGHCs avTools = do
|
||||||
ghcdir <- liftIO $ ghcupGHCBaseDir
|
ghcs <- getInstalledGHCs
|
||||||
fs <- liftIO $ getDirsFiles' ghcdir
|
fmap catMaybes $ forM ghcs $ \case
|
||||||
fmap catMaybes $ forM fs $ \(toFilePath -> f) -> do
|
Right tver@GHCTargetVersion{ _tvTarget = Nothing, .. } -> do
|
||||||
case version . decUTF8Safe $ f of
|
case Map.lookup _tvVersion avTools of
|
||||||
Right v' -> do
|
Just _ -> pure Nothing
|
||||||
case Map.lookup v' avTools of
|
Nothing -> do
|
||||||
Just _ -> pure Nothing
|
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet Nothing
|
||||||
Nothing -> do
|
fromSrc <- liftIO $ ghcSrcInstalled tver
|
||||||
lSet <- fmap (maybe False (== v')) $ ghcSet
|
pure $ Just $ ListResult
|
||||||
fromSrc <- liftIO $ ghcSrcInstalled v'
|
{ lTool = GHC
|
||||||
pure $ Just $ ListResult
|
, lVer = _tvVersion
|
||||||
{ lTool = GHC
|
, lCross = Nothing
|
||||||
, lVer = v'
|
, lTag = []
|
||||||
, lTag = []
|
, lInstalled = True
|
||||||
, lInstalled = True
|
, lStray = maybe True (const False) (Map.lookup _tvVersion avTools)
|
||||||
, lStray = maybe True (const False) (Map.lookup v' avTools)
|
, ..
|
||||||
, ..
|
}
|
||||||
}
|
Right tver@GHCTargetVersion{ .. } -> do
|
||||||
Left e -> do
|
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet _tvTarget
|
||||||
$(logWarn)
|
fromSrc <- liftIO $ ghcSrcInstalled tver
|
||||||
[i|Could not parse version of stray directory #{toFilePath ghcdir}/#{f}: #{e}|]
|
pure $ Just $ ListResult
|
||||||
pure Nothing
|
{ lTool = GHC
|
||||||
|
, lVer = _tvVersion
|
||||||
|
, lCross = _tvTarget
|
||||||
|
, lTag = []
|
||||||
|
, lInstalled = True
|
||||||
|
, lStray = True -- NOTE: cross currently cannot be installed via bindist
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
Left e -> do
|
||||||
|
$(logWarn)
|
||||||
|
[i|Could not parse version of stray directory #{toFilePath e}|]
|
||||||
|
pure Nothing
|
||||||
|
|
||||||
|
-- NOTE: this are not cross ones, because no bindists
|
||||||
toListResult :: Tool -> (Version, [Tag]) -> IO ListResult
|
toListResult :: Tool -> (Version, [Tag]) -> IO ListResult
|
||||||
toListResult t (v, tags) = case t of
|
toListResult t (v, tags) = case t of
|
||||||
GHC -> do
|
GHC -> do
|
||||||
lSet <- fmap (maybe False (== v)) $ ghcSet
|
let tver = mkTVer v
|
||||||
lInstalled <- ghcInstalled v
|
lSet <- fmap (maybe False (\(GHCTargetVersion _ v') -> v' == v)) $ ghcSet Nothing
|
||||||
fromSrc <- ghcSrcInstalled v
|
lInstalled <- ghcInstalled tver
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, lStray = False, .. }
|
fromSrc <- ghcSrcInstalled tver
|
||||||
|
pure ListResult { lVer = v, lCross = Nothing , lTag = tags, lTool = t, lStray = False, .. }
|
||||||
Cabal -> do
|
Cabal -> do
|
||||||
lSet <- fmap (== v) $ cabalSet
|
lSet <- fmap (== v) $ cabalSet
|
||||||
let lInstalled = lSet
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v
|
pure ListResult { lVer = v
|
||||||
|
, lCross = Nothing
|
||||||
, lTag = tags
|
, lTag = tags
|
||||||
, lTool = t
|
, lTool = t
|
||||||
, fromSrc = False
|
, fromSrc = False
|
||||||
@@ -394,6 +401,7 @@ listVersions av lt criteria = case lt of
|
|||||||
let lInstalled = lSet
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v
|
pure ListResult { lVer = v
|
||||||
, lTag = tags
|
, lTag = tags
|
||||||
|
, lCross = Nothing
|
||||||
, lTool = t
|
, lTool = t
|
||||||
, fromSrc = False
|
, fromSrc = False
|
||||||
, lStray = False
|
, lStray = False
|
||||||
@@ -416,10 +424,10 @@ listVersions av lt criteria = case lt of
|
|||||||
|
|
||||||
-- | This function may throw and crash in various ways.
|
-- | This function may throw and crash in various ways.
|
||||||
rmGHCVer :: (MonadThrow m, MonadLogger m, MonadIO m, MonadFail m)
|
rmGHCVer :: (MonadThrow m, MonadLogger m, MonadIO m, MonadFail m)
|
||||||
=> Version
|
=> GHCTargetVersion
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
rmGHCVer ver = do
|
rmGHCVer ver = do
|
||||||
isSetGHC <- fmap (maybe False (== ver)) $ ghcSet
|
isSetGHC <- fmap (maybe False (== ver)) $ ghcSet (_tvTarget ver)
|
||||||
dir <- liftIO $ ghcupGHCDir ver
|
dir <- liftIO $ ghcupGHCDir ver
|
||||||
let d' = toFilePath dir
|
let d' = toFilePath dir
|
||||||
exists <- liftIO $ doesDirectoryExist dir
|
exists <- liftIO $ doesDirectoryExist dir
|
||||||
@@ -430,7 +438,7 @@ rmGHCVer ver = do
|
|||||||
-- this isn't atomic, order matters
|
-- this isn't atomic, order matters
|
||||||
when isSetGHC $ do
|
when isSetGHC $ do
|
||||||
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
||||||
liftE $ rmPlain ver
|
liftE $ rmPlain (_tvTarget ver)
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Removing directory recursively: #{d'}|]
|
lift $ $(logInfo) [i|Removing directory recursively: #{d'}|]
|
||||||
liftIO $ deleteDirRecursive dir
|
liftIO $ deleteDirRecursive dir
|
||||||
@@ -442,15 +450,15 @@ rmGHCVer ver = do
|
|||||||
-- first remove
|
-- first remove
|
||||||
lift $ rmMajorSymlinks ver
|
lift $ rmMajorSymlinks ver
|
||||||
-- then fix them (e.g. with an earlier version)
|
-- then fix them (e.g. with an earlier version)
|
||||||
(mj, mi) <- getGHCMajor ver
|
(mj, mi) <- getMajorMinorV (_tvVersion ver)
|
||||||
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
getGHCForMajor mj mi (_tvTarget ver) >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
||||||
|
|
||||||
liftIO
|
liftIO
|
||||||
$ ghcupBaseDir
|
$ ghcupBaseDir
|
||||||
>>= hideError doesNotExistErrorType
|
>>= hideError doesNotExistErrorType
|
||||||
. deleteFile
|
. deleteFile
|
||||||
. (</> [rel|share|])
|
. (</> [rel|share|])
|
||||||
else throwE (NotInstalled GHC ver)
|
else throwE (NotInstalled GHC (ver ^. tvVersion % to prettyVer))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -491,11 +499,12 @@ compileGHC :: ( MonadMask m
|
|||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Version -- ^ version to install
|
-> GHCTargetVersion -- ^ version to install
|
||||||
-> Either Version (Path Abs) -- ^ version to bootstrap with
|
-> Either Version (Path Abs) -- ^ version to bootstrap with
|
||||||
-> Maybe Int -- ^ jobs
|
-> Maybe Int -- ^ jobs
|
||||||
-> Maybe (Path Abs) -- ^ build config
|
-> Maybe (Path Abs) -- ^ build config
|
||||||
-> Maybe (Path Abs)
|
-> Maybe (Path Abs) -- ^ patch directory
|
||||||
|
-> [Text] -- ^ additional args to ./configure
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ AlreadyInstalled
|
'[ AlreadyInstalled
|
||||||
, BuildFailed
|
, BuildFailed
|
||||||
@@ -512,13 +521,15 @@ compileGHC :: ( MonadMask m
|
|||||||
]
|
]
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
compileGHC dls tver bstrap jobs mbuildConfig patchdir = do
|
compileGHC dls tver bstrap jobs mbuildConfig patchdir aargs = do
|
||||||
lift $ $(logDebug) [i|Requested to compile: #{tver} with #{bstrap}|]
|
lift $ $(logDebug) [i|Requested to compile: #{tver} with #{bstrap}|]
|
||||||
whenM (liftIO $ toolAlreadyInstalled GHC tver)
|
whenM (liftIO $ ghcInstalled tver)
|
||||||
(throwE $ AlreadyInstalled GHC tver)
|
(throwE $ AlreadyInstalled GHC (tver ^. tvVersion))
|
||||||
|
|
||||||
-- download source tarball
|
-- download source tarball
|
||||||
dlInfo <- preview (ix GHC % ix tver % viSourceDL % _Just) dls ?? NoDownload
|
dlInfo <-
|
||||||
|
preview (ix GHC % ix (tver ^. tvVersion) % viSourceDL % _Just) dls
|
||||||
|
?? NoDownload
|
||||||
dl <- liftE $ downloadCached dlInfo Nothing
|
dl <- liftE $ downloadCached dlInfo Nothing
|
||||||
|
|
||||||
-- unpack
|
-- unpack
|
||||||
@@ -533,32 +544,29 @@ compileGHC dls tver bstrap jobs mbuildConfig patchdir = do
|
|||||||
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
||||||
ghcdir <- liftIO $ ghcupGHCDir tver
|
ghcdir <- liftIO $ ghcupGHCDir tver
|
||||||
|
|
||||||
-- Be careful about cleanup. We must catch both pure exceptions
|
liftE $ runBuildAction
|
||||||
-- as well as async ones.
|
tmpUnpack
|
||||||
flip onException
|
(Just ghcdir)
|
||||||
(liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
(compile bghc ghcdir workdir >> markSrcBuilt ghcdir workdir)
|
||||||
$ catchAllE
|
|
||||||
(\es ->
|
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
|
||||||
>> throwE (BuildFailed workdir es)
|
|
||||||
)
|
|
||||||
$ compile bghc ghcdir workdir
|
|
||||||
markSrcBuilt ghcdir workdir
|
|
||||||
|
|
||||||
-- only clean up dir if the build succeeded
|
|
||||||
liftIO $ deleteDirRecursive tmpUnpack
|
|
||||||
|
|
||||||
reThrowAll GHCupSetError $ postGHCInstall tver
|
reThrowAll GHCupSetError $ postGHCInstall tver
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
defaultConf = [s|
|
defaultConf = case _tvTarget tver of
|
||||||
|
Nothing -> [s|
|
||||||
V=0
|
V=0
|
||||||
BUILD_MAN = NO
|
BUILD_MAN = NO
|
||||||
BUILD_SPHINX_HTML = NO
|
BUILD_SPHINX_HTML = NO
|
||||||
BUILD_SPHINX_PDF = NO
|
BUILD_SPHINX_PDF = NO
|
||||||
HADDOCK_DOCS = YES
|
HADDOCK_DOCS = YES|]
|
||||||
GhcWithLlvmCodeGen = YES|]
|
Just _ -> [s|
|
||||||
|
V=0
|
||||||
|
BUILD_MAN = NO
|
||||||
|
BUILD_SPHINX_HTML = NO
|
||||||
|
BUILD_SPHINX_PDF = NO
|
||||||
|
HADDOCK_DOCS = NO
|
||||||
|
Stage1Only = YES|]
|
||||||
|
|
||||||
compile :: (MonadCatch m, MonadLogger m, MonadIO m)
|
compile :: (MonadCatch m, MonadLogger m, MonadIO m)
|
||||||
=> Either (Path Rel) (Path Abs)
|
=> Either (Path Rel) (Path Abs)
|
||||||
@@ -566,6 +574,7 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
-> Path Abs
|
-> Path Abs
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ FileDoesNotExistError
|
'[ FileDoesNotExistError
|
||||||
|
, InvalidBuildConfig
|
||||||
, PatchFailed
|
, PatchFailed
|
||||||
, ProcessError
|
, ProcessError
|
||||||
, NotFoundInPATH
|
, NotFoundInPATH
|
||||||
@@ -574,14 +583,14 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
()
|
()
|
||||||
compile bghc ghcdir workdir = do
|
compile bghc ghcdir workdir = do
|
||||||
lift $ $(logInfo) [i|configuring build|]
|
lift $ $(logInfo) [i|configuring build|]
|
||||||
|
liftE $ checkBuildConfig
|
||||||
|
|
||||||
forM_ patchdir $ \dir -> liftE $ applyPatches dir workdir
|
forM_ patchdir $ \dir -> liftE $ applyPatches dir workdir
|
||||||
|
|
||||||
-- force ld.bfd for build (others seem to misbehave, like lld from FreeBSD)
|
cEnv <- liftIO $ getEnvironment
|
||||||
newEnv <- addToCurrentEnv [("LD", "ld.bfd")]
|
|
||||||
|
|
||||||
if
|
if
|
||||||
| tver >= [vver|8.8.0|] -> do
|
| (_tvVersion tver) >= [vver|8.8.0|] -> do
|
||||||
bghcPath <- case bghc of
|
bghcPath <- case bghc of
|
||||||
Right ghc' -> pure ghc'
|
Right ghc' -> pure ghc'
|
||||||
Left bver -> do
|
Left bver -> do
|
||||||
@@ -590,20 +599,32 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
lEM $ liftIO $ execLogged
|
lEM $ liftIO $ execLogged
|
||||||
"./configure"
|
"./configure"
|
||||||
False
|
False
|
||||||
["--prefix=" <> toFilePath ghcdir]
|
( ["--prefix=" <> toFilePath ghcdir]
|
||||||
|
++ (maybe mempty
|
||||||
|
(\x -> ["--target=" <> E.encodeUtf8 x])
|
||||||
|
(_tvTarget tver)
|
||||||
|
)
|
||||||
|
++ fmap E.encodeUtf8 aargs
|
||||||
|
)
|
||||||
[rel|ghc-conf|]
|
[rel|ghc-conf|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just (("GHC", toFilePath bghcPath) : newEnv))
|
(Just (("GHC", toFilePath bghcPath) : cEnv))
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
lEM $ liftIO $ execLogged
|
lEM $ liftIO $ execLogged
|
||||||
"./configure"
|
"./configure"
|
||||||
False
|
False
|
||||||
[ "--prefix=" <> toFilePath ghcdir
|
( [ "--prefix=" <> toFilePath ghcdir
|
||||||
, "--with-ghc=" <> either toFilePath toFilePath bghc
|
, "--with-ghc=" <> either toFilePath toFilePath bghc
|
||||||
]
|
]
|
||||||
|
++ (maybe mempty
|
||||||
|
(\x -> ["--target=" <> E.encodeUtf8 x])
|
||||||
|
(_tvTarget tver)
|
||||||
|
)
|
||||||
|
++ fmap E.encodeUtf8 aargs
|
||||||
|
)
|
||||||
[rel|ghc-conf|]
|
[rel|ghc-conf|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just newEnv)
|
(Just cEnv)
|
||||||
|
|
||||||
case mbuildConfig of
|
case mbuildConfig of
|
||||||
Just bc -> liftIOException
|
Just bc -> liftIOException
|
||||||
@@ -626,6 +647,30 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
|
|
||||||
build_mk workdir = workdir </> [rel|mk/build.mk|]
|
build_mk workdir = workdir </> [rel|mk/build.mk|]
|
||||||
|
|
||||||
|
checkBuildConfig :: (MonadCatch m, MonadIO m)
|
||||||
|
=> Excepts
|
||||||
|
'[FileDoesNotExistError , InvalidBuildConfig]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
checkBuildConfig = do
|
||||||
|
c <- case mbuildConfig of
|
||||||
|
Just bc -> do
|
||||||
|
BL.toStrict <$> liftIOException doesNotExistErrorType
|
||||||
|
(FileDoesNotExistError $ toFilePath bc)
|
||||||
|
(liftIO $ readFile bc)
|
||||||
|
Nothing -> pure defaultConf
|
||||||
|
let lines' = fmap T.strip . T.lines $ decUTF8Safe c
|
||||||
|
|
||||||
|
-- for cross, we need Stage1Only
|
||||||
|
case _tvTarget tver of
|
||||||
|
Just _ -> when (not $ elem "Stage1Only = YES" lines') $ throwE
|
||||||
|
(InvalidBuildConfig
|
||||||
|
[s|Cross compiling needs to be a Stage1 build, add "Stage1Only = YES" to your config!|]
|
||||||
|
)
|
||||||
|
Nothing -> pure ()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
compileCabal :: ( MonadReader Settings m
|
compileCabal :: ( MonadReader Settings m
|
||||||
, MonadResource m
|
, MonadResource m
|
||||||
@@ -666,7 +711,11 @@ compileCabal dls tver bghc jobs patchdir = do
|
|||||||
|
|
||||||
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
||||||
|
|
||||||
reThrowAll (BuildFailed workdir) $ compile workdir
|
|
||||||
|
liftE $ runBuildAction
|
||||||
|
tmpUnpack
|
||||||
|
Nothing
|
||||||
|
(compile workdir)
|
||||||
|
|
||||||
-- only clean up dir if the build succeeded
|
-- only clean up dir if the build succeeded
|
||||||
liftIO $ deleteDirRecursive tmpUnpack
|
liftIO $ deleteDirRecursive tmpUnpack
|
||||||
@@ -781,12 +830,12 @@ upgradeGHCup dls mtarget force = do
|
|||||||
-- | Creates ghc-x.y.z and ghc-x.y symlinks. This is used for
|
-- | Creates ghc-x.y.z and ghc-x.y symlinks. This is used for
|
||||||
-- both installing from source and bindist.
|
-- both installing from source and bindist.
|
||||||
postGHCInstall :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
postGHCInstall :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> GHCTargetVersion
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
postGHCInstall ver = do
|
postGHCInstall ver@GHCTargetVersion{..} = do
|
||||||
void $ liftE $ setGHC ver SetGHC_XYZ
|
void $ liftE $ setGHC ver SetGHC_XYZ
|
||||||
|
|
||||||
-- Create ghc-x.y symlinks. This may not be the current
|
-- Create ghc-x.y symlinks. This may not be the current
|
||||||
-- version, create it regardless.
|
-- version, create it regardless.
|
||||||
(mj, mi) <- liftIO $ getGHCMajor ver
|
(mj, mi) <- getMajorMinorV _tvVersion
|
||||||
getGHCForMajor mj mi >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
getGHCForMajor mj mi _tvTarget >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCupDownloads where
|
module GHCup.Data.GHCupDownloads where
|
||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils.Version.QQ
|
import GHCup.Utils.Version.QQ
|
||||||
@@ -951,6 +951,24 @@ cabal_3000_64_darwin = DownloadInfo
|
|||||||
Nothing
|
Nothing
|
||||||
"d4857e068560515e4cbb0e8ca124c370e07892f2a28804d87152834e5fe2b845"
|
"d4857e068560515e4cbb0e8ca124c370e07892f2a28804d87152834e5fe2b845"
|
||||||
|
|
||||||
|
cabal_3000_64_freebsd :: DownloadInfo
|
||||||
|
cabal_3000_64_freebsd = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.0.0.0-x86_64-portbld-freebsd.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"d97b6469ed612a1367ad1032d0722469ee5277668879694d7d4336233b937516"
|
||||||
|
|
||||||
|
cabal_3000_32_alpine :: DownloadInfo
|
||||||
|
cabal_3000_32_alpine = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.0.0.0-i386-alpine-linux-musl.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"a4191cd5a645b00e6a9c53abe6f3cb91fe700de7d7c520c9cb36ce8ec5c9919a"
|
||||||
|
|
||||||
|
cabal_3000_64_alpine :: DownloadInfo
|
||||||
|
cabal_3000_64_alpine = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.0.0.0-x86_64-alpine-linux-musl.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"7b35e5986aba4a40fc37141cbde26612bfc916e95a2d2ff35a413612d8c7cd3a"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
@@ -976,6 +994,24 @@ cabal_3200_64_darwin = DownloadInfo
|
|||||||
Nothing
|
Nothing
|
||||||
"9197c17d2ece0f934f5b33e323cfcaf486e4681952687bc3d249488ce3cbe0e9"
|
"9197c17d2ece0f934f5b33e323cfcaf486e4681952687bc3d249488ce3cbe0e9"
|
||||||
|
|
||||||
|
cabal_3200_64_freebsd :: DownloadInfo
|
||||||
|
cabal_3200_64_freebsd = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"e4dc00ab7fef51354e7624dd03e49c6bb684887fc95acb9b33bc52f357a5ef8c"
|
||||||
|
|
||||||
|
cabal_3200_32_alpine :: DownloadInfo
|
||||||
|
cabal_3200_32_alpine = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-i386-alpine-linux-musl.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"4aaa52fbc337ae1ef855a2aa2808186580b21ec36883aafec7473e7d899bc5ec"
|
||||||
|
|
||||||
|
cabal_3200_64_alpine :: DownloadInfo
|
||||||
|
cabal_3200_64_alpine = DownloadInfo
|
||||||
|
[uri|https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-x86_64-alpine-linux-musl.tar.xz&dl=1|]
|
||||||
|
Nothing
|
||||||
|
"c1f3c21a5307cea8d2a0bd9a2eab9f56f3dd90e947ae64e231f909024980992b"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1026,7 +1062,7 @@ ghcupDownloads = M.fromList
|
|||||||
, M.fromList
|
, M.fromList
|
||||||
[ ( [vver|7.10.3|]
|
[ ( [vver|7.10.3|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.8.2.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/7.10.3/docs/html/users_guide/release-7-10-1.html|]
|
[uri|https://downloads.haskell.org/ghc/7.10.3/docs/html/users_guide/release-7-10-1.html|]
|
||||||
)
|
)
|
||||||
@@ -1072,7 +1108,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.0.2|]
|
, ( [vver|8.0.2|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.9.1.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.0.2/docs/html/users_guide/8.0.1-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.0.2/docs/html/users_guide/8.0.1-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1118,7 +1154,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.2.2|]
|
, ( [vver|8.2.2|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.10.1.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.2.2/docs/html/users_guide/8.2.2-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.2.2/docs/html/users_guide/8.2.2-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1170,7 +1206,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.4.1|]
|
, ( [vver|8.4.1|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.11.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.4.1/docs/html/users_guide/8.4.1-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.4.1/docs/html/users_guide/8.4.1-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1209,7 +1245,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.4.2|]
|
, ( [vver|8.4.2|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.11.1.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.4.2/docs/html/users_guide/8.4.2-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.4.2/docs/html/users_guide/8.4.2-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1259,7 +1295,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.4.3|]
|
, ( [vver|8.4.3|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.11.1.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.4.3/docs/html/users_guide/8.4.3-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.4.3/docs/html/users_guide/8.4.3-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1308,7 +1344,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.4.4|]
|
, ( [vver|8.4.4|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.11.1.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.4.4/docs/html/users_guide/8.4.4-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.4.4/docs/html/users_guide/8.4.4-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1362,7 +1398,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.6.1|]
|
, ( [vver|8.6.1|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.12.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.6.1/docs/html/users_guide/8.6.1-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.6.1/docs/html/users_guide/8.6.1-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1412,7 +1448,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.6.2|]
|
, ( [vver|8.6.2|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.12.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.6.2/docs/html/users_guide/8.6.2-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.6.2/docs/html/users_guide/8.6.2-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1456,7 +1492,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.6.3|]
|
, ( [vver|8.6.3|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.12.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.6.3/docs/html/users_guide/8.6.3-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.6.3/docs/html/users_guide/8.6.3-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1510,7 +1546,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.6.4|]
|
, ( [vver|8.6.4|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.12.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.6.4/docs/html/users_guide/8.6.4-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.6.4/docs/html/users_guide/8.6.4-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1559,7 +1595,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.6.5|]
|
, ( [vver|8.6.5|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.12.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.6.5/docs/html/users_guide/8.6.5-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.6.5/docs/html/users_guide/8.6.5-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1612,7 +1648,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.8.1|]
|
, ( [vver|8.8.1|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.13.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.8.1/docs/html/users_guide/8.8.1-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.8.1/docs/html/users_guide/8.8.1-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1665,7 +1701,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.8.2|]
|
, ( [vver|8.8.2|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[]
|
[Base [pver|4.13.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.8.2/docs/html/users_guide/8.8.2-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.8.2/docs/html/users_guide/8.8.2-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1718,7 +1754,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.8.3|]
|
, ( [vver|8.8.3|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[Recommended]
|
[Recommended, Base [pver|4.13.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.8.3/docs/html/users_guide/8.8.3-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.8.3/docs/html/users_guide/8.8.3-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1771,7 +1807,7 @@ ghcupDownloads = M.fromList
|
|||||||
)
|
)
|
||||||
, ( [vver|8.10.1|]
|
, ( [vver|8.10.1|]
|
||||||
, VersionInfo
|
, VersionInfo
|
||||||
[Latest]
|
[Latest, Base [pver|4.14.0.0|]]
|
||||||
(Just
|
(Just
|
||||||
[uri|https://downloads.haskell.org/ghc/8.10.1/docs/html/users_guide/8.10.1-notes.html|]
|
[uri|https://downloads.haskell.org/ghc/8.10.1/docs/html/users_guide/8.10.1-notes.html|]
|
||||||
)
|
)
|
||||||
@@ -1889,7 +1925,9 @@ ghcupDownloads = M.fromList
|
|||||||
[ ( Linux UnknownLinux
|
[ ( Linux UnknownLinux
|
||||||
, M.fromList [(Nothing, cabal_3000_64_linux)]
|
, M.fromList [(Nothing, cabal_3000_64_linux)]
|
||||||
)
|
)
|
||||||
, (Darwin, M.fromList [(Nothing, cabal_3000_64_darwin)])
|
, (Linux Alpine, M.fromList [(Nothing, cabal_3000_64_alpine)])
|
||||||
|
, (Darwin , M.fromList [(Nothing, cabal_3000_64_darwin)])
|
||||||
|
, (FreeBSD, M.fromList [(Nothing, cabal_3000_64_freebsd)])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
, ( A_32
|
, ( A_32
|
||||||
@@ -1897,6 +1935,7 @@ ghcupDownloads = M.fromList
|
|||||||
[ ( Linux UnknownLinux
|
[ ( Linux UnknownLinux
|
||||||
, M.fromList [(Nothing, cabal_3000_32_linux)]
|
, M.fromList [(Nothing, cabal_3000_32_linux)]
|
||||||
)
|
)
|
||||||
|
, (Linux Alpine, M.fromList [(Nothing, cabal_3000_32_alpine)])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -1918,7 +1957,9 @@ ghcupDownloads = M.fromList
|
|||||||
[ ( Linux UnknownLinux
|
[ ( Linux UnknownLinux
|
||||||
, M.fromList [(Nothing, cabal_3200_64_linux)]
|
, M.fromList [(Nothing, cabal_3200_64_linux)]
|
||||||
)
|
)
|
||||||
, (Darwin, M.fromList [(Nothing, cabal_3200_64_darwin)])
|
, (Linux Alpine, M.fromList [(Nothing, cabal_3200_64_alpine)])
|
||||||
|
, (Darwin , M.fromList [(Nothing, cabal_3200_64_darwin)])
|
||||||
|
, (FreeBSD, M.fromList [(Nothing, cabal_3200_64_freebsd)])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
, ( A_32
|
, ( A_32
|
||||||
@@ -1926,6 +1967,7 @@ ghcupDownloads = M.fromList
|
|||||||
[ ( Linux UnknownLinux
|
[ ( Linux UnknownLinux
|
||||||
, M.fromList [(Nothing, cabal_3200_32_linux)]
|
, M.fromList [(Nothing, cabal_3200_32_linux)]
|
||||||
)
|
)
|
||||||
|
, (Linux Alpine, M.fromList [(Nothing, cabal_3200_32_alpine)])
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
module GHCupInfo where
|
module GHCup.Data.GHCupInfo where
|
||||||
|
|
||||||
import GHCupDownloads
|
import GHCup.Data.GHCupDownloads
|
||||||
import ToolRequirements
|
import GHCup.Data.ToolRequirements
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
module ToolRequirements where
|
module GHCup.Data.ToolRequirements where
|
||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils.String.QQ
|
import GHCup.Utils.String.QQ
|
||||||
|
import GHCup.Utils.Version.QQ
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
@@ -61,6 +62,35 @@ toolRequirements = M.fromList
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
, ( Linux CentOS
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[ "gcc"
|
||||||
|
, "gcc-c++"
|
||||||
|
, "gmp"
|
||||||
|
, "make"
|
||||||
|
, "ncurses"
|
||||||
|
, "ncurses-compat-libs"
|
||||||
|
, "xz"
|
||||||
|
, "perl"
|
||||||
|
]
|
||||||
|
""
|
||||||
|
),
|
||||||
|
( Just [vers|7|]
|
||||||
|
, Requirements
|
||||||
|
[ "gcc"
|
||||||
|
, "gcc-c++"
|
||||||
|
, "gmp"
|
||||||
|
, "make"
|
||||||
|
, "ncurses"
|
||||||
|
, "xz"
|
||||||
|
, "perl"
|
||||||
|
]
|
||||||
|
""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
, ( Darwin
|
, ( Darwin
|
||||||
, M.fromList
|
, M.fromList
|
||||||
[ ( Nothing
|
[ ( Nothing
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
module GHCup.Download where
|
module GHCup.Download where
|
||||||
|
|
||||||
#if !defined(CURL)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
import GHCup.Download.IOStreams
|
import GHCup.Download.IOStreams
|
||||||
import GHCup.Download.Utils
|
import GHCup.Download.Utils
|
||||||
#endif
|
#endif
|
||||||
@@ -35,18 +35,19 @@ import Control.Monad.Reader
|
|||||||
import Control.Monad.Trans.Resource
|
import Control.Monad.Trans.Resource
|
||||||
hiding ( throwM )
|
hiding ( throwM )
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
#if !defined(CURL)
|
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
import Data.CaseInsensitive ( CI )
|
import Data.CaseInsensitive ( CI )
|
||||||
#endif
|
#endif
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
#if !defined(CURL)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
import Data.Time.Format
|
import Data.Time.Format
|
||||||
#endif
|
#endif
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
|
import Data.Word8
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO as HIO
|
import HPath.IO as HIO
|
||||||
@@ -57,12 +58,14 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
|
import System.Posix.Env.ByteString ( getEnv )
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
import qualified Crypto.Hash.SHA256 as SHA256
|
import qualified Crypto.Hash.SHA256 as SHA256
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Base16 as B16
|
import qualified Data.ByteString.Base16 as B16
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
#if !defined(CURL)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
import qualified Data.Map.Strict as M
|
import qualified Data.Map.Strict as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
@@ -82,6 +85,48 @@ import qualified System.Posix.RawFilePath.Directory
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Like 'getDownloads', but tries to fall back to
|
||||||
|
-- cached ~/.ghcup/cache/ghcup-<format-ver>.json
|
||||||
|
getDownloadsF :: ( FromJSONKey Tool
|
||||||
|
, FromJSONKey Version
|
||||||
|
, FromJSON VersionInfo
|
||||||
|
, MonadIO m
|
||||||
|
, MonadCatch m
|
||||||
|
, MonadLogger m
|
||||||
|
, MonadThrow m
|
||||||
|
, MonadFail m
|
||||||
|
, MonadReader Settings m
|
||||||
|
)
|
||||||
|
=> URLSource
|
||||||
|
-> Excepts
|
||||||
|
'[JSONError , DownloadFailed , FileDoesNotExistError]
|
||||||
|
m
|
||||||
|
GHCupInfo
|
||||||
|
getDownloadsF urlSource = do
|
||||||
|
case urlSource of
|
||||||
|
GHCupURL ->
|
||||||
|
liftE
|
||||||
|
$ handleIO (\_ -> readFromCache)
|
||||||
|
$ catchE @_ @'[JSONError , FileDoesNotExistError]
|
||||||
|
(\(DownloadFailed _) -> readFromCache)
|
||||||
|
$ getDownloads urlSource
|
||||||
|
(OwnSource _) -> liftE $ getDownloads urlSource
|
||||||
|
(OwnSpec _) -> liftE $ getDownloads urlSource
|
||||||
|
where
|
||||||
|
readFromCache = do
|
||||||
|
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
|
||||||
|
bs <-
|
||||||
|
handleIO' NoSuchThing
|
||||||
|
(\_ -> throwE $ FileDoesNotExistError (toFilePath json_file))
|
||||||
|
$ liftIO
|
||||||
|
$ readFile json_file
|
||||||
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
|
|
||||||
|
|
||||||
-- | Downloads the download information! But only if we need to ;P
|
-- | Downloads the download information! But only if we need to ;P
|
||||||
getDownloads :: ( FromJSONKey Tool
|
getDownloads :: ( FromJSONKey Tool
|
||||||
, FromJSONKey Version
|
, FromJSONKey Version
|
||||||
@@ -91,6 +136,7 @@ getDownloads :: ( FromJSONKey Tool
|
|||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadFail m
|
, MonadFail m
|
||||||
|
, MonadReader Settings m
|
||||||
)
|
)
|
||||||
=> URLSource
|
=> URLSource
|
||||||
-> Excepts '[JSONError , DownloadFailed] m GHCupInfo
|
-> Excepts '[JSONError , DownloadFailed] m GHCupInfo
|
||||||
@@ -116,7 +162,12 @@ getDownloads urlSource = do
|
|||||||
--
|
--
|
||||||
-- Always save the local file with the mod time of the remote file.
|
-- Always save the local file with the mod time of the remote file.
|
||||||
smartDl :: forall m1
|
smartDl :: forall m1
|
||||||
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
. ( MonadCatch m1
|
||||||
|
, MonadIO m1
|
||||||
|
, MonadFail m1
|
||||||
|
, MonadLogger m1
|
||||||
|
, MonadReader Settings m1
|
||||||
|
)
|
||||||
=> URI
|
=> URI
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ FileDoesNotExistError
|
'[ FileDoesNotExistError
|
||||||
@@ -148,31 +199,38 @@ getDownloads urlSource = do
|
|||||||
Just modTime -> do
|
Just modTime -> do
|
||||||
fileMod <- liftIO $ getModificationTime json_file
|
fileMod <- liftIO $ getModificationTime json_file
|
||||||
if modTime > fileMod
|
if modTime > fileMod
|
||||||
then do
|
then dlWithMod modTime json_file
|
||||||
bs <- liftE $ downloadBS uri'
|
|
||||||
liftIO $ writeFileWithModTime modTime json_file bs
|
|
||||||
pure bs
|
|
||||||
else liftIO $ readFile json_file
|
else liftIO $ readFile json_file
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
||||||
liftIO $ deleteFile json_file
|
dlWithoutMod json_file
|
||||||
liftE $ downloadBS uri'
|
|
||||||
else -- access in less than 5 minutes, re-use file
|
else -- access in less than 5 minutes, re-use file
|
||||||
liftIO $ readFile json_file
|
liftIO $ readFile json_file
|
||||||
else do
|
else do
|
||||||
liftIO $ createDirIfMissing newDirPerms cacheDir
|
liftIO $ createDirIfMissing newDirPerms cacheDir
|
||||||
getModTime >>= \case
|
getModTime >>= \case
|
||||||
Just modTime -> do
|
Just modTime -> dlWithMod modTime json_file
|
||||||
bs <- liftE $ downloadBS uri'
|
|
||||||
liftIO $ writeFileWithModTime modTime json_file bs
|
|
||||||
pure bs
|
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
-- although we don't know last-modified, we still save
|
||||||
|
-- it to a file, so we might use it in offline mode
|
||||||
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
||||||
liftE $ downloadBS uri'
|
dlWithoutMod json_file
|
||||||
|
|
||||||
where
|
where
|
||||||
|
dlWithMod modTime json_file = do
|
||||||
|
bs <- liftE $ downloadBS uri'
|
||||||
|
liftIO $ writeFileWithModTime modTime json_file bs
|
||||||
|
pure bs
|
||||||
|
dlWithoutMod json_file = do
|
||||||
|
bs <- liftE $ downloadBS uri'
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile json_file
|
||||||
|
liftIO $ writeFileL json_file (Just newFilePerms) bs
|
||||||
|
liftIO $ setModificationTime json_file (fromIntegral @Int 0)
|
||||||
|
pure bs
|
||||||
|
|
||||||
|
|
||||||
getModTime = do
|
getModTime = do
|
||||||
#if defined(CURL)
|
#if !defined(INTERNAL_DOWNLOADER)
|
||||||
pure Nothing
|
pure Nothing
|
||||||
#else
|
#else
|
||||||
headers <-
|
headers <-
|
||||||
@@ -271,12 +329,19 @@ download dli dest mfn
|
|||||||
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
||||||
>> (throwE . DownloadFailed $ e)
|
>> (throwE . DownloadFailed $ e)
|
||||||
) $ do
|
) $ do
|
||||||
#if defined(CURL)
|
lift getDownloader >>= \case
|
||||||
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "curl" True
|
Curl -> do
|
||||||
["-fL", "-o", toFilePath destFile , serializeURIRef' $ view dlUri dli] Nothing Nothing
|
o' <- liftIO getCurlOpts
|
||||||
#else
|
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "curl" True
|
||||||
(https, host, fullPath, port) <- liftE $ uriToQuadruple (view dlUri dli)
|
(o' ++ ["-fL", "-o", toFilePath destFile, serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
||||||
liftE $ downloadToFile https host fullPath port destFile
|
Wget -> do
|
||||||
|
o' <- liftIO getWgetOpts
|
||||||
|
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "wget" True
|
||||||
|
(o' ++ ["-O", toFilePath destFile , serializeURIRef' $ view dlUri dli]) Nothing Nothing
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
Internal -> do
|
||||||
|
(https, host, fullPath, port) <- liftE $ uriToQuadruple (view dlUri dli)
|
||||||
|
liftE $ downloadToFile https host fullPath port destFile
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
liftE $ checkDigest dli destFile
|
liftE $ checkDigest dli destFile
|
||||||
@@ -329,7 +394,7 @@ downloadCached dli mfn = do
|
|||||||
|
|
||||||
|
|
||||||
-- | This is used for downloading the JSON.
|
-- | This is used for downloading the JSON.
|
||||||
downloadBS :: (MonadCatch m, MonadIO m)
|
downloadBS :: (MonadReader Settings m, MonadCatch m, MonadIO m, MonadLogger m)
|
||||||
=> URI
|
=> URI
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ FileDoesNotExistError
|
'[ FileDoesNotExistError
|
||||||
@@ -356,18 +421,33 @@ downloadBS uri'
|
|||||||
where
|
where
|
||||||
scheme = view (uriSchemeL' % schemeBSL') uri'
|
scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||||
path = view pathL' uri'
|
path = view pathL' uri'
|
||||||
#if defined(CURL)
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
dl _ = do
|
|
||||||
let exe = [rel|curl|]
|
|
||||||
args = ["-sSfL", serializeURIRef' uri']
|
|
||||||
liftIO (executeOut exe args Nothing) >>= \case
|
|
||||||
CapturedProcess ExitSuccess stdout _ -> do
|
|
||||||
pure $ L.fromStrict stdout
|
|
||||||
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' (toFilePath exe) args
|
|
||||||
#else
|
|
||||||
dl https = do
|
dl https = do
|
||||||
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
#else
|
||||||
liftE $ downloadBS' https host' fullPath' port'
|
dl _ = do
|
||||||
|
#endif
|
||||||
|
lift $ $(logDebug) [i|downloading: #{serializeURIRef' uri'}|]
|
||||||
|
lift getDownloader >>= \case
|
||||||
|
Curl -> do
|
||||||
|
o' <- liftIO getCurlOpts
|
||||||
|
let exe = [rel|curl|]
|
||||||
|
args = o' ++ ["-sSfL", serializeURIRef' uri']
|
||||||
|
liftIO (executeOut exe args Nothing) >>= \case
|
||||||
|
CapturedProcess ExitSuccess stdout _ -> do
|
||||||
|
pure $ L.fromStrict stdout
|
||||||
|
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' (toFilePath exe) args
|
||||||
|
Wget -> do
|
||||||
|
o' <- liftIO getWgetOpts
|
||||||
|
let exe = [rel|wget|]
|
||||||
|
args = o' ++ ["-qO-", serializeURIRef' uri']
|
||||||
|
liftIO (executeOut exe args Nothing) >>= \case
|
||||||
|
CapturedProcess ExitSuccess stdout _ -> do
|
||||||
|
pure $ L.fromStrict stdout
|
||||||
|
CapturedProcess (ExitFailure i') _ _ -> throwE $ NonZeroExit i' (toFilePath exe) args
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
Internal -> do
|
||||||
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
liftE $ downloadBS' https host' fullPath' port'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -385,3 +465,19 @@ checkDigest dli file = do
|
|||||||
let eDigest = view dlHash dli
|
let eDigest = view dlHash dli
|
||||||
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
when ((cDigest /= eDigest) && verify) $ throwE (DigestError cDigest eDigest)
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get additional curl args from env. This is an undocumented option.
|
||||||
|
getCurlOpts :: IO [ByteString]
|
||||||
|
getCurlOpts =
|
||||||
|
getEnv "GHCUP_CURL_OPTS" >>= \case
|
||||||
|
Just r -> pure $ BS.split _space r
|
||||||
|
Nothing -> pure []
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get additional wget args from env. This is an undocumented option.
|
||||||
|
getWgetOpts :: IO [ByteString]
|
||||||
|
getWgetOpts =
|
||||||
|
getEnv "GHCUP_WGET_OPTS" >>= \case
|
||||||
|
Just r -> pure $ BS.split _space r
|
||||||
|
Nothing -> pure []
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ data AlreadyInstalled = AlreadyInstalled Tool Version
|
|||||||
|
|
||||||
-- | The tool is not installed. Some operations rely on a tool
|
-- | The tool is not installed. Some operations rely on a tool
|
||||||
-- to be installed (such as setting the current GHC version).
|
-- to be installed (such as setting the current GHC version).
|
||||||
data NotInstalled = NotInstalled Tool Version
|
data NotInstalled = NotInstalled Tool Text
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
-- | An executable was expected to be in PATH, but was not found.
|
-- | An executable was expected to be in PATH, but was not found.
|
||||||
@@ -104,6 +104,9 @@ data PatchFailed = PatchFailed
|
|||||||
data NoToolRequirements = NoToolRequirements
|
data NoToolRequirements = NoToolRequirements
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
data InvalidBuildConfig = InvalidBuildConfig Text
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
--[ High-level errors ]--
|
--[ High-level errors ]--
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module GHCup.Types where
|
module GHCup.Types where
|
||||||
|
|
||||||
@@ -81,7 +83,9 @@ data VersionInfo = VersionInfo
|
|||||||
-- | A tag. These are currently attached to a version of a tool.
|
-- | A tag. These are currently attached to a version of a tool.
|
||||||
data Tag = Latest
|
data Tag = Latest
|
||||||
| Recommended
|
| Recommended
|
||||||
deriving (Ord, Eq, Show)
|
| Base PVP
|
||||||
|
| UnknownTag String -- ^ used for upwardscompat
|
||||||
|
deriving (Ord, Eq, Show) -- FIXME: manual JSON instance
|
||||||
|
|
||||||
|
|
||||||
data Architecture = A_64
|
data Architecture = A_64
|
||||||
@@ -138,12 +142,26 @@ data URLSource = GHCupURL
|
|||||||
|
|
||||||
|
|
||||||
data Settings = Settings
|
data Settings = Settings
|
||||||
{ cache :: Bool
|
{ cache :: Bool
|
||||||
, noVerify :: Bool
|
, noVerify :: Bool
|
||||||
|
, keepDirs :: KeepDirs
|
||||||
|
, downloader :: Downloader
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
|
data KeepDirs = Always
|
||||||
|
| Errors
|
||||||
|
| Never
|
||||||
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
|
data Downloader = Curl
|
||||||
|
| Wget
|
||||||
|
#if defined(INTERNAL_DOWNLOADER)
|
||||||
|
| Internal
|
||||||
|
#endif
|
||||||
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
data DebugInfo = DebugInfo
|
data DebugInfo = DebugInfo
|
||||||
{ diBaseDir :: Path Abs
|
{ diBaseDir :: Path Abs
|
||||||
, diBinDir :: Path Abs
|
, diBinDir :: Path Abs
|
||||||
@@ -173,3 +191,23 @@ data PlatformRequest = PlatformRequest
|
|||||||
, _rVersion :: Maybe Versioning
|
, _rVersion :: Maybe Versioning
|
||||||
}
|
}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
|
-- | A GHC identified by the target platform triple
|
||||||
|
-- and the version.
|
||||||
|
data GHCTargetVersion = GHCTargetVersion
|
||||||
|
{ _tvTarget :: Maybe Text
|
||||||
|
, _tvVersion :: Version
|
||||||
|
}
|
||||||
|
deriving (Ord, Eq, Show)
|
||||||
|
|
||||||
|
|
||||||
|
mkTVer :: Version -> GHCTargetVersion
|
||||||
|
mkTVer = GHCTargetVersion Nothing
|
||||||
|
|
||||||
|
|
||||||
|
-- | Assembles a path of the form: <target-triple>-<version>
|
||||||
|
prettyTVer :: GHCTargetVersion -> Text
|
||||||
|
prettyTVer (GHCTargetVersion (Just t) v') = t <> "-" <> prettyVer v'
|
||||||
|
prettyTVer (GHCTargetVersion Nothing v') = prettyVer v'
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,24 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Tool
|
|||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VSep
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VSep
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VUnit
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VUnit
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Tag
|
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupInfo
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupInfo
|
||||||
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Requirements
|
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Requirements
|
||||||
|
|
||||||
|
instance ToJSON Tag where
|
||||||
|
toJSON Latest = String "Latest"
|
||||||
|
toJSON Recommended = String "Recommended"
|
||||||
|
toJSON (Base pvp'') = String ("base-" <> prettyPVP pvp'')
|
||||||
|
toJSON (UnknownTag x ) = String (T.pack x)
|
||||||
|
|
||||||
|
instance FromJSON Tag where
|
||||||
|
parseJSON = withText "Tag" $ \t -> case T.unpack t of
|
||||||
|
"Latest" -> pure Latest
|
||||||
|
"Recommended" -> pure Recommended
|
||||||
|
('b' : 'a' : 's' : 'e' : '-' : ver') -> case pvp (T.pack ver') of
|
||||||
|
Right x -> pure $ Base x
|
||||||
|
Left e -> fail . show $ e
|
||||||
|
x -> pure (UnknownTag x)
|
||||||
|
|
||||||
instance ToJSON URI where
|
instance ToJSON URI where
|
||||||
toJSON = toJSON . decUTF8Safe . serializeURIRef'
|
toJSON = toJSON . decUTF8Safe . serializeURIRef'
|
||||||
@@ -143,6 +156,14 @@ instance FromJSONKey Version where
|
|||||||
Right x -> pure x
|
Right x -> pure x
|
||||||
Left e -> fail $ "Failure in Version (FromJSONKey)" <> show e
|
Left e -> fail $ "Failure in Version (FromJSONKey)" <> show e
|
||||||
|
|
||||||
|
instance ToJSON PVP where
|
||||||
|
toJSON = toJSON . prettyPVP
|
||||||
|
|
||||||
|
instance FromJSON PVP where
|
||||||
|
parseJSON = withText "PVP" $ \t -> case pvp t of
|
||||||
|
Right x -> pure x
|
||||||
|
Left e -> fail $ "Failure in PVP (FromJSON)" <> show e
|
||||||
|
|
||||||
instance ToJSONKey Tool where
|
instance ToJSONKey Tool where
|
||||||
toJSONKey = genericToJSONKey defaultJSONKeyOptions
|
toJSONKey = genericToJSONKey defaultJSONKeyOptions
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ makeLenses ''DownloadInfo
|
|||||||
makeLenses ''Tag
|
makeLenses ''Tag
|
||||||
makeLenses ''VersionInfo
|
makeLenses ''VersionInfo
|
||||||
|
|
||||||
|
makeLenses ''GHCTargetVersion
|
||||||
|
|
||||||
makeLenses ''GHCupInfo
|
makeLenses ''GHCupInfo
|
||||||
|
|
||||||
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCup.Utils
|
module GHCup.Utils
|
||||||
@@ -19,7 +20,9 @@ import GHCup.Types.Optics
|
|||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
import GHCup.Utils.Dirs
|
import GHCup.Utils.Dirs
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
|
import GHCup.Utils.MegaParsec
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
|
import GHCup.Utils.String.QQ
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -29,11 +32,12 @@ import Control.Monad.Fail ( MonadFail )
|
|||||||
#endif
|
#endif
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.Attoparsec.ByteString
|
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
|
import Data.Either
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
import Data.Word8
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
@@ -51,6 +55,7 @@ import System.Posix.FilePath ( getSearchPath
|
|||||||
, takeFileName
|
, takeFileName
|
||||||
)
|
)
|
||||||
import System.Posix.Files.ByteString ( readSymbolicLink )
|
import System.Posix.Files.ByteString ( readSymbolicLink )
|
||||||
|
import Text.Regex.Posix
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
import qualified Codec.Archive.Tar as Tar
|
import qualified Codec.Archive.Tar as Tar
|
||||||
@@ -60,7 +65,7 @@ import qualified Codec.Compression.Lzma as Lzma
|
|||||||
import qualified Data.ByteString as B
|
import qualified Data.ByteString as B
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
|
import qualified Text.Megaparsec as MP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -73,64 +78,69 @@ import qualified Data.Text.Encoding as E
|
|||||||
|
|
||||||
-- | The symlink destination of a ghc tool.
|
-- | The symlink destination of a ghc tool.
|
||||||
ghcLinkDestination :: ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
ghcLinkDestination :: ByteString -- ^ the tool, such as 'ghc', 'haddock' etc.
|
||||||
-> Version
|
-> GHCTargetVersion
|
||||||
-> ByteString
|
-> ByteString
|
||||||
ghcLinkDestination tool ver = "../ghc/" <> verToBS ver <> "/bin/" <> tool
|
ghcLinkDestination tool ver =
|
||||||
|
"../ghc/" <> E.encodeUtf8 (prettyTVer ver) <> "/bin/" <> tool
|
||||||
|
|
||||||
-- | Extract the version part of the result of `ghcLinkDestination`.
|
|
||||||
ghcLinkVersion :: MonadThrow m => ByteString -> m Version
|
|
||||||
ghcLinkVersion = either (throwM . ParseError) pure . parseOnly parser
|
|
||||||
where
|
|
||||||
parser = string "../ghc/" *> verParser <* string "/bin/ghc"
|
|
||||||
verParser = many1' (notWord8 _slash) >>= \t ->
|
|
||||||
case
|
|
||||||
version (decUTF8Safe $ B.pack t)
|
|
||||||
of
|
|
||||||
Left e -> fail $ show e
|
|
||||||
Right r -> pure r
|
|
||||||
|
|
||||||
|
|
||||||
-- e.g. ghc-8.6.5
|
-- e.g. ghc-8.6.5
|
||||||
rmMinorSymlinks :: (MonadIO m, MonadLogger m) => Version -> m ()
|
rmMinorSymlinks :: (MonadIO m, MonadLogger m) => GHCTargetVersion -> m ()
|
||||||
rmMinorSymlinks ver = do
|
rmMinorSymlinks GHCTargetVersion {..} = do
|
||||||
bindir <- liftIO $ ghcupBinDir
|
bindir <- liftIO $ ghcupBinDir
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
|
||||||
let myfiles =
|
files <- liftIO $ findFiles'
|
||||||
filter (\x -> ("-" <> verToBS ver) `B.isSuffixOf` toFilePath x) files
|
bindir
|
||||||
forM_ myfiles $ \f -> do
|
( maybe mempty (\x -> MP.chunk (x <> "-")) _tvTarget
|
||||||
|
*> parseUntil1 (MP.chunk $ prettyVer _tvVersion)
|
||||||
|
*> (MP.chunk $ prettyVer _tvVersion)
|
||||||
|
*> MP.eof
|
||||||
|
)
|
||||||
|
|
||||||
|
forM_ files $ \f -> do
|
||||||
let fullF = (bindir </> f)
|
let fullF = (bindir </> f)
|
||||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
|
|
||||||
-- E.g. ghc, if this version is the set one.
|
|
||||||
-- This reads `ghcupGHCDir`.
|
-- Removes the set ghc version for the given target, if any.
|
||||||
rmPlain :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
rmPlain :: (MonadLogger m, MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> Maybe Text -- ^ target
|
||||||
-> Excepts '[NotInstalled] m ()
|
-> Excepts '[NotInstalled] m ()
|
||||||
rmPlain ver = do
|
rmPlain target = do
|
||||||
files <- liftE $ ghcToolFiles ver
|
mtv <- ghcSet target
|
||||||
bindir <- liftIO $ ghcupBinDir
|
forM_ mtv $ \tv -> do
|
||||||
forM_ files $ \f -> do
|
files <- liftE $ ghcToolFiles tv
|
||||||
let fullF = (bindir </> f)
|
bindir <- liftIO $ ghcupBinDir
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
forM_ files $ \f -> do
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
let fullF = (bindir </> f)
|
||||||
-- old ghcup
|
lift $ $(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
let hdc_file = (bindir </> [rel|haddock-ghc|])
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
lift $ $(logDebug) [i|rm -f #{toFilePath hdc_file}|]
|
-- old ghcup
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile hdc_file
|
let hdc_file = (bindir </> [rel|haddock-ghc|])
|
||||||
|
lift $ $(logDebug) [i|rm -f #{toFilePath hdc_file}|]
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteFile hdc_file
|
||||||
|
|
||||||
|
|
||||||
-- e.g. ghc-8.6
|
-- e.g. ghc-8.6
|
||||||
rmMajorSymlinks :: (MonadLogger m, MonadIO m) => Version -> m ()
|
rmMajorSymlinks :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
rmMajorSymlinks ver = do
|
=> GHCTargetVersion
|
||||||
(mj, mi) <- liftIO $ getGHCMajor ver
|
-> m ()
|
||||||
let v' = E.encodeUtf8 $ intToText mj <> "." <> intToText mi
|
rmMajorSymlinks GHCTargetVersion {..} = do
|
||||||
|
(mj, mi) <- getMajorMinorV _tvVersion
|
||||||
|
let v' = intToText mj <> "." <> intToText mi
|
||||||
|
|
||||||
bindir <- liftIO ghcupBinDir
|
bindir <- liftIO ghcupBinDir
|
||||||
|
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
files <- liftIO $ findFiles'
|
||||||
let myfiles = filter (\x -> ("-" <> v') `B.isSuffixOf` toFilePath x) files
|
bindir
|
||||||
forM_ myfiles $ \f -> do
|
( maybe mempty (\x -> MP.chunk (x <> "-")) _tvTarget
|
||||||
|
*> parseUntil1 (MP.chunk v')
|
||||||
|
*> MP.chunk v'
|
||||||
|
*> MP.eof
|
||||||
|
)
|
||||||
|
|
||||||
|
forM_ files $ \f -> do
|
||||||
let fullF = (bindir </> f)
|
let fullF = (bindir </> f)
|
||||||
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
$(logDebug) [i|rm -f #{toFilePath fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
@@ -143,33 +153,61 @@ rmMajorSymlinks ver = do
|
|||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
|
||||||
toolAlreadyInstalled :: Tool -> Version -> IO Bool
|
ghcInstalled :: GHCTargetVersion -> IO Bool
|
||||||
toolAlreadyInstalled tool ver = case tool of
|
|
||||||
GHC -> ghcInstalled ver
|
|
||||||
Cabal -> cabalInstalled ver
|
|
||||||
GHCup -> pure True
|
|
||||||
|
|
||||||
|
|
||||||
ghcInstalled :: Version -> IO Bool
|
|
||||||
ghcInstalled ver = do
|
ghcInstalled ver = do
|
||||||
ghcdir <- ghcupGHCDir ver
|
ghcdir <- ghcupGHCDir ver
|
||||||
doesDirectoryExist ghcdir
|
doesDirectoryExist ghcdir
|
||||||
|
|
||||||
|
|
||||||
ghcSrcInstalled :: Version -> IO Bool
|
ghcSrcInstalled :: GHCTargetVersion -> IO Bool
|
||||||
ghcSrcInstalled ver = do
|
ghcSrcInstalled ver = do
|
||||||
ghcdir <- ghcupGHCDir ver
|
ghcdir <- ghcupGHCDir ver
|
||||||
doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
|
||||||
|
|
||||||
|
|
||||||
ghcSet :: (MonadIO m) => m (Maybe Version)
|
ghcSet :: (MonadThrow m, MonadIO m)
|
||||||
ghcSet = do
|
=> Maybe Text -- ^ the target of the GHC version, if any
|
||||||
ghcBin <- (</> [rel|ghc|]) <$> liftIO ghcupBinDir
|
-- (e.g. armv7-unknown-linux-gnueabihf)
|
||||||
|
-> m (Maybe GHCTargetVersion)
|
||||||
|
ghcSet mtarget = do
|
||||||
|
ghc <- parseRel $ E.encodeUtf8 (maybe "ghc" (<> "-ghc") mtarget)
|
||||||
|
ghcBin <- (</> ghc) <$> liftIO ghcupBinDir
|
||||||
|
|
||||||
-- link destination is of the form ../ghc/<ver>/bin/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
|
liftIO $ handleIO' NoSuchThing (\_ -> pure $ Nothing) $ do
|
||||||
link <- readSymbolicLink $ toFilePath ghcBin
|
link <- readSymbolicLink $ toFilePath ghcBin
|
||||||
Just <$> ghcLinkVersion link
|
Just <$> ghcLinkVersion link
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
-- | 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 = do
|
||||||
|
ghcdir <- liftIO $ ghcupGHCBaseDir
|
||||||
|
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ getDirsFiles' ghcdir
|
||||||
|
forM fs $ \f -> case parseGHCupGHCDir f of
|
||||||
|
Right r -> pure $ Right r
|
||||||
|
Left _ -> pure $ Left f
|
||||||
|
|
||||||
|
|
||||||
cabalInstalled :: Version -> IO Bool
|
cabalInstalled :: Version -> IO Bool
|
||||||
@@ -193,33 +231,49 @@ cabalSet = do
|
|||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|
||||||
-- | We assume GHC is in semver format. I hope it is.
|
getMajorMinorV :: MonadThrow m => Version -> m (Int, Int)
|
||||||
getGHCMajor :: MonadThrow m => Version -> m (Int, Int)
|
getMajorMinorV Version {..} = case _vChunks of
|
||||||
getGHCMajor ver = do
|
([Digits x] : [Digits y] : _) -> pure (fromIntegral x, fromIntegral y)
|
||||||
SemVer {..} <- throwEither (semver $ prettyVer ver)
|
_ -> throwM $ ParseError "Could not parse X.Y from version"
|
||||||
pure (fromIntegral _svMajor, fromIntegral _svMinor)
|
|
||||||
|
|
||||||
|
matchMajor :: Version -> Int -> Int -> Bool
|
||||||
|
matchMajor v' major' minor' = case getMajorMinorV v' of
|
||||||
|
Just (x, y) -> x == major' && y == minor'
|
||||||
|
Nothing -> False
|
||||||
|
|
||||||
|
|
||||||
-- | Get the latest installed full GHC version that satisfies X.Y.
|
-- | Get the latest installed full GHC version that satisfies X.Y.
|
||||||
-- This reads `ghcupGHCBaseDir`.
|
-- This reads `ghcupGHCBaseDir`.
|
||||||
getGHCForMajor :: (MonadIO m, MonadThrow m)
|
getGHCForMajor :: (MonadIO m, MonadThrow m)
|
||||||
=> Int -- ^ major version component
|
=> Int -- ^ major version component
|
||||||
-> Int -- ^ minor version component
|
-> Int -- ^ minor version component
|
||||||
-> m (Maybe Version)
|
-> Maybe Text -- ^ the target triple
|
||||||
getGHCForMajor major' minor' = do
|
-> m (Maybe GHCTargetVersion)
|
||||||
p <- liftIO $ ghcupGHCBaseDir
|
getGHCForMajor major' minor' mt = do
|
||||||
ghcs <- liftIO $ getDirsFiles' p
|
ghcs <- rights <$> getInstalledGHCs
|
||||||
semvers <- forM ghcs $ \ghc ->
|
|
||||||
throwEither . semver =<< (throwEither . E.decodeUtf8' . toFilePath $ ghc)
|
pure
|
||||||
mapM (throwEither . version)
|
|
||||||
. fmap prettySemVer
|
|
||||||
. lastMay
|
. lastMay
|
||||||
. sort
|
. sortBy (\x y -> compare (_tvVersion x) (_tvVersion y))
|
||||||
. filter
|
. filter
|
||||||
(\SemVer {..} ->
|
(\GHCTargetVersion {..} ->
|
||||||
fromIntegral _svMajor == major' && fromIntegral _svMinor == minor'
|
_tvTarget == mt && matchMajor _tvVersion major' minor'
|
||||||
)
|
)
|
||||||
$ semvers
|
$ ghcs
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get the latest available ghc for X.Y major version.
|
||||||
|
getLatestGHCFor :: Int -- ^ major version component
|
||||||
|
-> Int -- ^ minor version component
|
||||||
|
-> GHCupDownloads
|
||||||
|
-> Maybe Version
|
||||||
|
getLatestGHCFor major' minor' dls = do
|
||||||
|
join
|
||||||
|
. fmap (lastMay . filter (\v -> matchMajor v major' minor'))
|
||||||
|
. preview (ix GHC % to Map.keys)
|
||||||
|
$ dls
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -265,7 +319,8 @@ unpackToDir dest av = do
|
|||||||
|
|
||||||
-- | Get the tool version that has this tag. If multiple have it,
|
-- | Get the tool version that has this tag. If multiple have it,
|
||||||
-- picks the greatest version.
|
-- picks the greatest version.
|
||||||
getTagged :: Tag -> AffineFold (Map.Map Version VersionInfo) (Version, VersionInfo)
|
getTagged :: Tag
|
||||||
|
-> AffineFold (Map.Map Version VersionInfo) (Version, VersionInfo)
|
||||||
getTagged tag =
|
getTagged tag =
|
||||||
( to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
( to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
||||||
% to Map.toDescList
|
% to Map.toDescList
|
||||||
@@ -279,6 +334,12 @@ getRecommended :: GHCupDownloads -> Tool -> Maybe Version
|
|||||||
getRecommended av tool = headOf (ix tool % getTagged Recommended % to fst) $ av
|
getRecommended av tool = headOf (ix tool % getTagged Recommended % to fst) $ av
|
||||||
|
|
||||||
|
|
||||||
|
-- | Gets the latest GHC with a given base version.
|
||||||
|
getLatestBaseVersion :: GHCupDownloads -> PVP -> Maybe Version
|
||||||
|
getLatestBaseVersion av pvpVer =
|
||||||
|
headOf (ix GHC % getTagged (Base pvpVer) % to fst) av
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------
|
-----------------------
|
||||||
--[ Settings Getter ]--
|
--[ Settings Getter ]--
|
||||||
@@ -289,6 +350,10 @@ getCache :: MonadReader Settings m => m Bool
|
|||||||
getCache = ask <&> cache
|
getCache = ask <&> cache
|
||||||
|
|
||||||
|
|
||||||
|
getDownloader :: MonadReader Settings m => m Downloader
|
||||||
|
getDownloader = ask <&> downloader
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
--[ Other ]--
|
--[ Other ]--
|
||||||
@@ -302,12 +367,12 @@ urlBaseName = parseRel . snd . B.breakEnd (== _slash) . urlDecode False
|
|||||||
|
|
||||||
|
|
||||||
-- Get tool files from '~/.ghcup/bin/ghc/<ver>/bin/*'
|
-- Get tool files from '~/.ghcup/bin/ghc/<ver>/bin/*'
|
||||||
-- while ignoring *-<ver> symlinks.
|
-- while ignoring *-<ver> symlinks and accounting for cross triple prefix.
|
||||||
--
|
--
|
||||||
-- Returns unversioned relative files, e.g.:
|
-- Returns unversioned relative files, e.g.:
|
||||||
-- ["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]
|
-- ["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]
|
||||||
ghcToolFiles :: (MonadThrow m, MonadFail m, MonadIO m)
|
ghcToolFiles :: (MonadThrow m, MonadFail m, MonadIO m)
|
||||||
=> Version
|
=> GHCTargetVersion
|
||||||
-> Excepts '[NotInstalled] m [Path Rel]
|
-> Excepts '[NotInstalled] m [Path Rel]
|
||||||
ghcToolFiles ver = do
|
ghcToolFiles ver = do
|
||||||
ghcdir <- liftIO $ ghcupGHCDir ver
|
ghcdir <- liftIO $ ghcupGHCDir ver
|
||||||
@@ -315,18 +380,28 @@ ghcToolFiles ver = do
|
|||||||
|
|
||||||
-- fail if ghc is not installed
|
-- fail if ghc is not installed
|
||||||
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
|
||||||
(throwE (NotInstalled GHC ver))
|
(throwE (NotInstalled GHC (prettyTVer ver)))
|
||||||
|
|
||||||
files <- liftIO $ getDirsFiles' bindir
|
files <- liftIO $ getDirsFiles' bindir
|
||||||
-- figure out the <ver> suffix, because this might not be `Version` for
|
-- figure out the <ver> suffix, because this might not be `Version` for
|
||||||
-- alpha/rc releases, but x.y.a.somedate.
|
-- alpha/rc releases, but x.y.a.somedate.
|
||||||
|
|
||||||
|
-- for cross, this won't be "ghc", but e.g.
|
||||||
|
-- "armv7-unknown-linux-gnueabihf-ghc"
|
||||||
|
[ghcbin] <- liftIO $ findFiles
|
||||||
|
bindir
|
||||||
|
(makeRegexOpts compExtended
|
||||||
|
execBlank
|
||||||
|
([s|^([a-zA-Z0-9_-]*[a-zA-Z0-9_]-)?ghc$|] :: ByteString)
|
||||||
|
)
|
||||||
|
|
||||||
(Just symver) <-
|
(Just symver) <-
|
||||||
(B.stripPrefix "ghc-" . takeFileName)
|
(B.stripPrefix (toFilePath ghcbin <> "-") . takeFileName)
|
||||||
<$> (liftIO $ readSymbolicLink $ toFilePath (bindir </> [rel|ghc|]))
|
<$> (liftIO $ readSymbolicLink $ toFilePath (bindir </> ghcbin))
|
||||||
when (B.null symver)
|
when (B.null symver)
|
||||||
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
(throwIO $ userError $ "Fatal: ghc symlink target is broken")
|
||||||
|
|
||||||
pure $ filter (\x -> not $ symver `B.isSuffixOf` toFilePath x) files
|
pure . filter (\x -> not $ symver `B.isSuffixOf` toFilePath x) $ files
|
||||||
|
|
||||||
|
|
||||||
-- | This file, when residing in ~/.ghcup/ghc/<ver>/ signals that
|
-- | This file, when residing in ~/.ghcup/ghc/<ver>/ signals that
|
||||||
@@ -377,10 +452,42 @@ darwinNotarization _ _ = pure $ Right ()
|
|||||||
getChangeLog :: GHCupDownloads -> Tool -> Either Version Tag -> Maybe URI
|
getChangeLog :: GHCupDownloads -> Tool -> Either Version Tag -> Maybe URI
|
||||||
getChangeLog dls tool (Left v') =
|
getChangeLog dls tool (Left v') =
|
||||||
preview (ix tool % ix v' % viChangeLog % _Just) dls
|
preview (ix tool % ix v' % viChangeLog % _Just) dls
|
||||||
getChangeLog dls tool (Right tag) = preview
|
getChangeLog dls tool (Right tag) =
|
||||||
( ix tool
|
preview (ix tool % getTagged tag % to snd % viChangeLog % _Just) dls
|
||||||
% getTagged tag
|
|
||||||
% to snd
|
|
||||||
% viChangeLog
|
-- | Execute a build action while potentially cleaning up:
|
||||||
% _Just
|
--
|
||||||
) dls
|
-- 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)
|
||||||
|
-> Excepts e m ()
|
||||||
|
-> Excepts '[BuildFailed] m ()
|
||||||
|
runBuildAction bdir instdir action = do
|
||||||
|
Settings {..} <- lift ask
|
||||||
|
flip
|
||||||
|
onException
|
||||||
|
(do
|
||||||
|
forM_ instdir $ \dir ->
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive dir
|
||||||
|
when (keepDirs == Never)
|
||||||
|
$ liftIO
|
||||||
|
$ hideError doesNotExistErrorType
|
||||||
|
$ deleteDirRecursive bdir
|
||||||
|
)
|
||||||
|
$ catchAllE
|
||||||
|
(\es -> do
|
||||||
|
forM_ instdir $ \dir ->
|
||||||
|
liftIO $ hideError doesNotExistErrorType $ deleteDirRecursive dir
|
||||||
|
when (keepDirs == Never)
|
||||||
|
$ liftIO
|
||||||
|
$ hideError doesNotExistErrorType
|
||||||
|
$ deleteDirRecursive bdir
|
||||||
|
throwE (BuildFailed bdir es)
|
||||||
|
)
|
||||||
|
$ action
|
||||||
|
|
||||||
|
when (keepDirs == Never || keepDirs == Errors) $ liftIO $ deleteDirRecursive
|
||||||
|
bdir
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
module GHCup.Utils.Dirs where
|
module GHCup.Utils.Dirs where
|
||||||
|
|
||||||
|
|
||||||
|
import GHCup.Types
|
||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
|
import GHCup.Utils.MegaParsec
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
@@ -13,7 +16,6 @@ import Control.Monad
|
|||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Resource
|
import Control.Monad.Trans.Resource
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Versions
|
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO
|
import HPath.IO
|
||||||
import Optics
|
import Optics
|
||||||
@@ -27,8 +29,10 @@ import System.Posix.Env.ByteString ( getEnv
|
|||||||
import System.Posix.Temp.ByteString ( mkdtemp )
|
import System.Posix.Temp.ByteString ( mkdtemp )
|
||||||
|
|
||||||
import qualified Data.ByteString.UTF8 as UTF8
|
import qualified Data.ByteString.UTF8 as UTF8
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.Posix.FilePath as FP
|
import qualified System.Posix.FilePath as FP
|
||||||
import qualified System.Posix.User as PU
|
import qualified System.Posix.User as PU
|
||||||
|
import qualified Text.Megaparsec as MP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -37,6 +41,7 @@ import qualified System.Posix.User as PU
|
|||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | ~/.ghcup by default
|
||||||
ghcupBaseDir :: IO (Path Abs)
|
ghcupBaseDir :: IO (Path Abs)
|
||||||
ghcupBaseDir = do
|
ghcupBaseDir = do
|
||||||
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||||
@@ -44,16 +49,30 @@ ghcupBaseDir = do
|
|||||||
Nothing -> liftIO getHomeDirectory
|
Nothing -> liftIO getHomeDirectory
|
||||||
pure (bdir </> [rel|.ghcup|])
|
pure (bdir </> [rel|.ghcup|])
|
||||||
|
|
||||||
|
|
||||||
|
-- | ~/.ghcup/ghc by default.
|
||||||
ghcupGHCBaseDir :: IO (Path Abs)
|
ghcupGHCBaseDir :: IO (Path Abs)
|
||||||
ghcupGHCBaseDir = ghcupBaseDir <&> (</> [rel|ghc|])
|
ghcupGHCBaseDir = ghcupBaseDir <&> (</> [rel|ghc|])
|
||||||
|
|
||||||
ghcupGHCDir :: Version -> IO (Path Abs)
|
|
||||||
|
-- | 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 ver = do
|
ghcupGHCDir ver = do
|
||||||
ghcbasedir <- ghcupGHCBaseDir
|
ghcbasedir <- ghcupGHCBaseDir
|
||||||
verdir <- parseRel (verToBS ver)
|
verdir <- parseRel $ E.encodeUtf8 (prettyTVer ver)
|
||||||
pure (ghcbasedir </> verdir)
|
pure (ghcbasedir </> verdir)
|
||||||
|
|
||||||
|
|
||||||
|
-- | See 'ghcupToolParser'.
|
||||||
|
parseGHCupGHCDir :: MonadThrow m => Path Rel -> m GHCTargetVersion
|
||||||
|
parseGHCupGHCDir (toFilePath -> f) = do
|
||||||
|
fp <- throwEither $ E.decodeUtf8' f
|
||||||
|
throwEither $ MP.parse ghcTargetVerP "" fp
|
||||||
|
|
||||||
|
|
||||||
ghcupBinDir :: IO (Path Abs)
|
ghcupBinDir :: IO (Path Abs)
|
||||||
ghcupBinDir = ghcupBaseDir <&> (</> [rel|bin|])
|
ghcupBinDir = ghcupBaseDir <&> (</> [rel|bin|])
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import Data.Foldable
|
|||||||
import Data.Functor
|
import Data.Functor
|
||||||
import Data.IORef
|
import Data.IORef
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.Text ( Text )
|
||||||
|
import Data.Void
|
||||||
import GHC.Foreign ( peekCStringLen )
|
import GHC.Foreign ( peekCStringLen )
|
||||||
import GHC.IO.Encoding ( getLocaleEncoding )
|
import GHC.IO.Encoding ( getLocaleEncoding )
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
@@ -39,10 +41,12 @@ import "unix" System.Posix.IO.ByteString
|
|||||||
hiding ( openFd )
|
hiding ( openFd )
|
||||||
import System.Posix.Process ( ProcessStatus(..) )
|
import System.Posix.Process ( ProcessStatus(..) )
|
||||||
import System.Posix.Types
|
import System.Posix.Types
|
||||||
|
import Text.Regex.Posix
|
||||||
|
|
||||||
|
|
||||||
import qualified Control.Exception as EX
|
import qualified Control.Exception as EX
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.Posix.Process.ByteString
|
import qualified System.Posix.Process.ByteString
|
||||||
as SPPB
|
as SPPB
|
||||||
import Streamly.External.Posix.DirStream
|
import Streamly.External.Posix.DirStream
|
||||||
@@ -51,12 +55,14 @@ import qualified Streamly.Internal.Memory.ArrayStream
|
|||||||
import qualified Streamly.FileSystem.Handle as FH
|
import qualified Streamly.FileSystem.Handle as FH
|
||||||
import qualified Streamly.Internal.Data.Unfold as SU
|
import qualified Streamly.Internal.Data.Unfold as SU
|
||||||
import qualified Streamly.Prelude as S
|
import qualified Streamly.Prelude as S
|
||||||
|
import qualified Text.Megaparsec as MP
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
||||||
as SPIB
|
as SPIB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Bool signals whether the regions should be cleaned.
|
-- | Bool signals whether the regions should be cleaned.
|
||||||
data StopThread = StopThread Bool
|
data StopThread = StopThread Bool
|
||||||
deriving Show
|
deriving Show
|
||||||
@@ -199,6 +205,7 @@ execLogged exe spath args lfile chdir env = do
|
|||||||
lineAction ref rs bs' = do
|
lineAction ref rs bs' = do
|
||||||
modifyIORef' ref (swapRegs bs')
|
modifyIORef' ref (swapRegs bs')
|
||||||
regs <- readIORef ref
|
regs <- readIORef ref
|
||||||
|
void $ SPIB.fdWrite fileFd (bs' <> "\n")
|
||||||
forM (zip regs rs) $ \(bs, r) -> do
|
forM (zip regs rs) $ \(bs, r) -> do
|
||||||
setConsoleRegion r $ do
|
setConsoleRegion r $ do
|
||||||
w <- consoleWidth
|
w <- consoleWidth
|
||||||
@@ -210,8 +217,6 @@ execLogged exe spath args lfile chdir env = do
|
|||||||
. trim w
|
. trim w
|
||||||
. (\b -> "[ " <> toFilePath lfile <> " ] " <> b)
|
. (\b -> "[ " <> toFilePath lfile <> " ] " <> b)
|
||||||
$ bs
|
$ bs
|
||||||
SPIB.fdWrite fileFd (bs <> "\n")
|
|
||||||
|
|
||||||
|
|
||||||
swapRegs bs regs | length regs < size = regs ++ [bs]
|
swapRegs bs regs | length regs < size = regs ++ [bs]
|
||||||
| otherwise = tail regs ++ [bs]
|
| otherwise = tail regs ++ [bs]
|
||||||
@@ -380,3 +385,27 @@ searchPath paths needle = go paths
|
|||||||
if p == toFilePath needle
|
if p == toFilePath needle
|
||||||
then isExecutable (basedir </> needle)
|
then isExecutable (basedir </> needle)
|
||||||
else pure False
|
else pure False
|
||||||
|
|
||||||
|
|
||||||
|
findFiles :: Path Abs -> Regex -> IO [Path Rel]
|
||||||
|
findFiles path regex = do
|
||||||
|
dirStream <- openDirStream (toFilePath path)
|
||||||
|
f <-
|
||||||
|
(fmap . fmap) snd
|
||||||
|
. S.toList
|
||||||
|
. S.filter (\(_, p) -> match regex p)
|
||||||
|
$ dirContentsStream dirStream
|
||||||
|
pure $ join $ fmap parseRel f
|
||||||
|
|
||||||
|
|
||||||
|
findFiles' :: Path Abs -> MP.Parsec Void Text () -> IO [Path Rel]
|
||||||
|
findFiles' path parser = do
|
||||||
|
dirStream <- openDirStream (toFilePath path)
|
||||||
|
f <-
|
||||||
|
(fmap . fmap) snd
|
||||||
|
. S.toList
|
||||||
|
. S.filter (\(_, p) -> case E.decodeUtf8' p of
|
||||||
|
Left _ -> False
|
||||||
|
Right p' -> isJust $ MP.parseMaybe parser p')
|
||||||
|
$ dirContentsStream dirStream
|
||||||
|
pure $ join $ fmap parseRel f
|
||||||
|
|||||||
87
lib/GHCup/Utils/MegaParsec.hs
Normal file
87
lib/GHCup/Utils/MegaParsec.hs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module GHCup.Utils.MegaParsec where
|
||||||
|
|
||||||
|
import GHCup.Types
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
|
import Control.Monad.Fail ( MonadFail )
|
||||||
|
#endif
|
||||||
|
import Data.Functor
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Text ( Text )
|
||||||
|
import Data.Versions
|
||||||
|
import Data.Void
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Text.Megaparsec as MP
|
||||||
|
|
||||||
|
|
||||||
|
choice' :: (MonadFail f, MP.MonadParsec e s f) => [f a] -> f a
|
||||||
|
choice' [] = fail "Empty list"
|
||||||
|
choice' [x ] = x
|
||||||
|
choice' (x : xs) = MP.try x <|> choice' xs
|
||||||
|
|
||||||
|
|
||||||
|
parseUntil :: MP.Parsec Void Text a -> MP.Parsec Void Text Text
|
||||||
|
parseUntil p = do
|
||||||
|
(MP.try (MP.lookAhead p) $> mempty)
|
||||||
|
<|> (do
|
||||||
|
c <- T.singleton <$> MP.anySingle
|
||||||
|
c2 <- parseUntil p
|
||||||
|
pure (c `mappend` c2)
|
||||||
|
)
|
||||||
|
|
||||||
|
parseUntil1 :: MP.Parsec Void Text a -> MP.Parsec Void Text Text
|
||||||
|
parseUntil1 p = do
|
||||||
|
i1 <- MP.getOffset
|
||||||
|
t <- parseUntil p
|
||||||
|
i2 <- MP.getOffset
|
||||||
|
if i1 == i2 then fail "empty parse" else pure t
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Parses e.g.
|
||||||
|
-- * armv7-unknown-linux-gnueabihf-ghc
|
||||||
|
-- * armv7-unknown-linux-gnueabihf-ghci
|
||||||
|
ghcTargetBinP :: Text -> MP.Parsec Void Text (Maybe Text, Text)
|
||||||
|
ghcTargetBinP t =
|
||||||
|
(,)
|
||||||
|
<$> ( MP.try
|
||||||
|
(Just <$> (parseUntil1 (MP.chunk "-" *> MP.chunk t)) <* MP.chunk "-"
|
||||||
|
)
|
||||||
|
<|> (flip const Nothing <$> mempty)
|
||||||
|
)
|
||||||
|
<*> (MP.chunk t <* MP.eof)
|
||||||
|
|
||||||
|
|
||||||
|
-- | Extracts target triple and version from e.g.
|
||||||
|
-- * armv7-unknown-linux-gnueabihf-8.8.3
|
||||||
|
-- * armv7-unknown-linux-gnueabihf-8.8.3
|
||||||
|
ghcTargetVerP :: MP.Parsec Void Text GHCTargetVersion
|
||||||
|
ghcTargetVerP =
|
||||||
|
(\x y -> GHCTargetVersion x y)
|
||||||
|
<$> (MP.try (Just <$> (parseUntil1 (MP.chunk "-" *> verP)) <* MP.chunk "-")
|
||||||
|
<|> (flip const Nothing <$> mempty)
|
||||||
|
)
|
||||||
|
<*> (version' <* MP.eof)
|
||||||
|
where
|
||||||
|
verP :: MP.Parsec Void Text Text
|
||||||
|
verP = do
|
||||||
|
v <- version'
|
||||||
|
let startsWithDigists =
|
||||||
|
and
|
||||||
|
. take 3
|
||||||
|
. join
|
||||||
|
. (fmap . fmap)
|
||||||
|
(\case
|
||||||
|
(Digits _) -> True
|
||||||
|
(Str _) -> False
|
||||||
|
)
|
||||||
|
$ (_vChunks v)
|
||||||
|
if startsWithDigists && not (isJust (_vEpoch v))
|
||||||
|
then pure $ prettyVer v
|
||||||
|
else fail "Oh"
|
||||||
@@ -218,6 +218,12 @@ throwEither a = case a of
|
|||||||
Right r -> pure r
|
Right r -> pure r
|
||||||
|
|
||||||
|
|
||||||
|
throwEither' :: (Exception a, MonadThrow m) => a -> Either x b -> m b
|
||||||
|
throwEither' e eth = case eth of
|
||||||
|
Left _ -> throwM e
|
||||||
|
Right r -> pure r
|
||||||
|
|
||||||
|
|
||||||
verToBS :: Version -> ByteString
|
verToBS :: Version -> ByteString
|
||||||
verToBS = E.encodeUtf8 . prettyVer
|
verToBS = E.encodeUtf8 . prettyVer
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import qualified Data.Text as T
|
|||||||
|
|
||||||
-- | This reflects the API version of the JSON.
|
-- | This reflects the API version of the JSON.
|
||||||
ghcupURL :: URI
|
ghcupURL :: URI
|
||||||
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.1.json|]
|
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.2.json|]
|
||||||
|
|
||||||
ghcUpVer :: PVP
|
ghcUpVer :: PVP
|
||||||
ghcUpVer = [pver|0.1.4|]
|
ghcUpVer = [pver|0.1.5|]
|
||||||
|
|
||||||
numericVer :: String
|
numericVer :: String
|
||||||
numericVer = T.unpack . prettyPVP $ ghcUpVer
|
numericVer = T.unpack . prettyPVP $ ghcUpVer
|
||||||
|
|||||||
14
shell-completions/bash
Normal file
14
shell-completions/bash
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
_ghcup()
|
||||||
|
{
|
||||||
|
local CMDLINE
|
||||||
|
local IFS=$'\n'
|
||||||
|
CMDLINE=(--bash-completion-index $COMP_CWORD)
|
||||||
|
|
||||||
|
for arg in ${COMP_WORDS[@]}; do
|
||||||
|
CMDLINE=(${CMDLINE[@]} --bash-completion-word $arg)
|
||||||
|
done
|
||||||
|
|
||||||
|
COMPREPLY=( $(ghcup "${CMDLINE[@]}") )
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -o filenames -F _ghcup ghcup
|
||||||
19
shell-completions/fish
Normal file
19
shell-completions/fish
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
function _ghcup
|
||||||
|
set -l cl (commandline --tokenize --current-process)
|
||||||
|
# Hack around fish issue #3934
|
||||||
|
set -l cn (commandline --tokenize --cut-at-cursor --current-process)
|
||||||
|
set -l cn (count $cn)
|
||||||
|
set -l tmpline --bash-completion-enriched --bash-completion-index $cn
|
||||||
|
for arg in $cl
|
||||||
|
set tmpline $tmpline --bash-completion-word $arg
|
||||||
|
end
|
||||||
|
for opt in (ghcup $tmpline)
|
||||||
|
if test -d $opt
|
||||||
|
echo -E "$opt/"
|
||||||
|
else
|
||||||
|
echo -E "$opt"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
complete --no-files --command ghcup --arguments '(_ghcup)'
|
||||||
32
shell-completions/zsh
Normal file
32
shell-completions/zsh
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#compdef ghcup
|
||||||
|
|
||||||
|
local request
|
||||||
|
local completions
|
||||||
|
local word
|
||||||
|
local index=$((CURRENT - 1))
|
||||||
|
|
||||||
|
request=(--bash-completion-enriched --bash-completion-index $index)
|
||||||
|
for arg in ${words[@]}; do
|
||||||
|
request=(${request[@]} --bash-completion-word $arg)
|
||||||
|
done
|
||||||
|
|
||||||
|
IFS=$'\n' completions=($( ghcup "${request[@]}" ))
|
||||||
|
|
||||||
|
for word in $completions; do
|
||||||
|
local -a parts
|
||||||
|
|
||||||
|
# Split the line at a tab if there is one.
|
||||||
|
IFS=$'\t' parts=($( echo $word ))
|
||||||
|
|
||||||
|
if [[ -n $parts[2] ]]; then
|
||||||
|
if [[ $word[1] == "-" ]]; then
|
||||||
|
local desc=("$parts[1] ($parts[2])")
|
||||||
|
compadd -d desc -- $parts[1]
|
||||||
|
else
|
||||||
|
local desc=($(print -f "%-019s -- %s" $parts[1] $parts[2]))
|
||||||
|
compadd -l -d desc -- $parts[1]
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
compadd -f -- $word
|
||||||
|
fi
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user