Compare commits
135 Commits
a93aaf9a5f
...
v0.1.5-alp
| Author | SHA1 | Date | |
|---|---|---|---|
| e88a39de27 | |||
| bc0cd22433 | |||
| ed4ff15f96 | |||
| 1d623723a2 | |||
| 931080244f | |||
| 27e2e7f848 | |||
| 8b638c7ecb | |||
| acd370611f | |||
| e1b5a89cee | |||
| 5edebd57d9 | |||
| bcaccaaf31 | |||
| 818a5d2d85 | |||
| 13acce07d4 | |||
| 4ed5e21b7f | |||
| 86aab6bb59 | |||
| 7f5cb64b18 | |||
| 6c12eb16eb | |||
| e637f90fae | |||
| 5b33c3f491 | |||
| 1842ed464f | |||
| 296bbdd561 | |||
| 27ead1be7c | |||
| 5184609dba | |||
| 5d94d0bf75 | |||
| 72bcfa9270 | |||
| fafff9dadd | |||
| e3c20d53a8 | |||
| 8b7dc68491 | |||
| 7742fe08b5 | |||
| a773da037c | |||
| dfeb814dcc | |||
| 0623c7b1b1 | |||
| 62005f83a4 | |||
| eaafd77a7e | |||
| 9d9e415a09 | |||
| 6c1ae585b7 | |||
| 793aad7b6c | |||
| fd7807a66e | |||
| 879bd061dd | |||
| 75632b2cf1 | |||
| b65b9dc5e1 | |||
| 31d70e34e9 | |||
| 84de282655 | |||
| 997dcadf89 | |||
| b2312629ce | |||
| 3d10f964c6 | |||
| 404038edcb | |||
| ea4f9ceab1 | |||
| 5c481ea94e | |||
| 1ccaf4ba91 | |||
| b532511cd5 | |||
| b3105b439c | |||
| 2b6cb5f1a8 | |||
| f4242b10e7 | |||
| ad4d185ead | |||
| b18aafe2c4 | |||
| 340196bf9d | |||
| 883226aa70 | |||
| 0d393612a7 | |||
| 5635f6cc4e | |||
| a7fd36beeb | |||
| baee1d5b85 | |||
| 68df6b8e50 | |||
| ac73090784 | |||
| faf4f3b7ca | |||
| d888d11d59 | |||
| 28a1077833 | |||
| c40b9dbc0b | |||
| 6bbd262818 | |||
| 78d36bce24 | |||
| aedfc19220 | |||
| 2f34fc7bef | |||
| de66b92631 | |||
| fee3984bf7 | |||
| b953c8fd30 | |||
| 24e4c3a19b | |||
| d2efb504b9 | |||
| df9dd0e785 | |||
| 89c9699158 | |||
| 124ddcdfeb | |||
| 5c0a0fc155 | |||
| b11b74d2b4 | |||
| 5ac8f5b651 | |||
| 9032df97cf | |||
| 14e1077ad1 | |||
| b5648bdd6b | |||
| e7cd952970 | |||
| 1455c2c175 | |||
| c106dd3f65 | |||
| f6725fbf5f | |||
| c706a047ea | |||
| 9602db31ab | |||
| c2c47e1b7e | |||
| 34386680cc | |||
| 16a26d9881 | |||
| 3496f24f6e | |||
| 1a5876a074 | |||
| c782bc44de | |||
| f78e7b1cbc | |||
| adec7b2398 | |||
| 958bf698b9 | |||
| 6a79782650 | |||
| 5382fd9aca | |||
| 8a0236a350 | |||
| 3e52def226 | |||
| 5d3c26b509 | |||
| 99941bc2a1 | |||
| 63f290107c | |||
| 31a8316bfa | |||
| 3ff6be5435 | |||
| 0963081fd8 | |||
| af42598a27 | |||
| e6037b9eb5 | |||
| e58e1c1954 | |||
| c7a831a280 | |||
| e77ed1a26c | |||
| c0c70f5c9b | |||
| fee16758de | |||
| f8448cf02b | |||
| 35b6359c1b | |||
| 9c7d17800d | |||
| ee570c024c | |||
| fcb7129251 | |||
| 8a1bd45ffe | |||
| f5a2db6719 | |||
| 2c99070d89 | |||
| 93aac16fc5 | |||
| 775c541895 | |||
| b0eba1a77a | |||
| 8aa2be5898 | |||
| 951a7173ae | |||
| b7f49b1c94 | |||
| dcd6812fb7 | |||
| 167826dfce | |||
| 03ee8915fb |
14
.bash-completion
Normal file
14
.bash-completion
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
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
dist-newstyle/
|
dist-newstyle/
|
||||||
|
.stack-work/
|
||||||
|
|||||||
197
.gitlab-ci.yml
Normal file
197
.gitlab-ci.yml
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
variables:
|
||||||
|
GIT_SSL_NO_VERIFY: "1"
|
||||||
|
|
||||||
|
# Commit of ghc/ci-images repository from which to pull Docker images
|
||||||
|
DOCKER_REV: cefaee3c742af193e0f7783f87edb0d35374515c
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# CI Step
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
.debian:
|
||||||
|
image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb9:$DOCKER_REV"
|
||||||
|
tags:
|
||||||
|
- x86_64-linux
|
||||||
|
variables:
|
||||||
|
OS: "LINUX"
|
||||||
|
|
||||||
|
.alpine:64bit:
|
||||||
|
image: "alpine:edge"
|
||||||
|
tags:
|
||||||
|
- x86_64-linux
|
||||||
|
variables:
|
||||||
|
OS: "LINUX"
|
||||||
|
BIT: "64"
|
||||||
|
|
||||||
|
.alpine:32bit:
|
||||||
|
image: "i386/alpine:edge"
|
||||||
|
tags:
|
||||||
|
- x86_64-linux
|
||||||
|
variables:
|
||||||
|
OS: "LINUX"
|
||||||
|
BIT: "32"
|
||||||
|
|
||||||
|
.darwin:
|
||||||
|
tags:
|
||||||
|
- x86_64-darwin
|
||||||
|
variables:
|
||||||
|
OS: "DARWIN"
|
||||||
|
|
||||||
|
.freebsd:
|
||||||
|
tags:
|
||||||
|
- x86_64-freebsd
|
||||||
|
variables:
|
||||||
|
OS: "FREEBSD"
|
||||||
|
|
||||||
|
.root_cleanup:
|
||||||
|
after_script:
|
||||||
|
- BUILD_DIR=$CI_PROJECT_DIR
|
||||||
|
- echo "Cleaning $BUILD_DIR"
|
||||||
|
- cd $HOME
|
||||||
|
- test -n "$BUILD_DIR"
|
||||||
|
- shopt -s extglob
|
||||||
|
- rm -Rf "$BUILD_DIR"/!(out)
|
||||||
|
- exit 0
|
||||||
|
|
||||||
|
.test_ghcup_version:
|
||||||
|
script:
|
||||||
|
- ./.gitlab/script/ghcup_version.sh
|
||||||
|
variables:
|
||||||
|
JSON_VERSION: "0.0.2"
|
||||||
|
|
||||||
|
.test_ghcup_version:linux:
|
||||||
|
extends:
|
||||||
|
- .test_ghcup_version
|
||||||
|
- .debian
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/linux/install_deps.sh
|
||||||
|
|
||||||
|
.test_ghcup_version:darwin:
|
||||||
|
extends:
|
||||||
|
- .test_ghcup_version
|
||||||
|
- .darwin
|
||||||
|
- .root_cleanup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/darwin/install_deps.sh
|
||||||
|
|
||||||
|
.test_ghcup_version:freebsd:
|
||||||
|
extends:
|
||||||
|
- .test_ghcup_version
|
||||||
|
- .freebsd
|
||||||
|
- .root_cleanup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/freebsd/install_deps.sh
|
||||||
|
|
||||||
|
.release_ghcup:
|
||||||
|
script:
|
||||||
|
- ./.gitlab/script/ghcup_release.sh
|
||||||
|
artifacts:
|
||||||
|
expire_in: 2 week
|
||||||
|
paths:
|
||||||
|
- out
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
|
||||||
|
######## linux test ########
|
||||||
|
|
||||||
|
test:linux:recommended:
|
||||||
|
extends: .test_ghcup_version:linux
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.6.5"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
test:linux:latest:
|
||||||
|
extends: .test_ghcup_version:linux
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
|
||||||
|
######## darwin test ########
|
||||||
|
|
||||||
|
test:mac:recommended:
|
||||||
|
extends: .test_ghcup_version:darwin
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.6.5"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
test:mac:latest:
|
||||||
|
extends: .test_ghcup_version:darwin
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
|
||||||
|
######## freebsd test ########
|
||||||
|
|
||||||
|
test:freebsd:recommended:
|
||||||
|
extends: .test_ghcup_version:freebsd
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.6.5"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
test:freebsd:latest:
|
||||||
|
extends: .test_ghcup_version:freebsd
|
||||||
|
variables:
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
|
||||||
|
######## linux release ########
|
||||||
|
|
||||||
|
release:linux:64bit:
|
||||||
|
extends:
|
||||||
|
- .alpine:64bit
|
||||||
|
- .release_ghcup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
||||||
|
variables:
|
||||||
|
ARTIFACT: "x86_64-linux-ghcup"
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
release:linux:32bit:
|
||||||
|
extends:
|
||||||
|
- .alpine:32bit
|
||||||
|
- .release_ghcup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/linux/alpine/install_deps.sh
|
||||||
|
variables:
|
||||||
|
ARTIFACT: "i386-linux-ghcup"
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
######## darwin release ########
|
||||||
|
|
||||||
|
release:darwin:
|
||||||
|
extends:
|
||||||
|
- .darwin
|
||||||
|
- .release_ghcup
|
||||||
|
- .root_cleanup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/darwin/install_deps.sh
|
||||||
|
variables:
|
||||||
|
ARTIFACT: "x86_64-apple-darwin-ghcup"
|
||||||
|
GHC_VERSION: "8.8.3"
|
||||||
|
CABAL_VERSION: "3.2.0.0"
|
||||||
|
MACOSX_DEPLOYMENT_TARGET: "10.7"
|
||||||
|
|
||||||
|
|
||||||
|
######## freebsd release ########
|
||||||
|
|
||||||
|
release:freebsd:
|
||||||
|
extends:
|
||||||
|
- .freebsd
|
||||||
|
- .release_ghcup
|
||||||
|
- .root_cleanup
|
||||||
|
before_script:
|
||||||
|
- ./.gitlab/before_script/freebsd/install_deps.sh
|
||||||
|
variables:
|
||||||
|
ARTIFACT: "x86_64-portbld-freebsd-ghcup"
|
||||||
|
GHC_VERSION: "8.6.5"
|
||||||
|
|
||||||
14
.gitlab/before_script/darwin/install_deps.sh
Executable file
14
.gitlab/before_script/darwin/install_deps.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../../ghcup_env"
|
||||||
|
|
||||||
|
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-apple-darwin-ghcup > ./ghcup-bin
|
||||||
|
chmod +x ghcup-bin
|
||||||
|
|
||||||
|
./ghcup-bin install ${GHC_VERSION}
|
||||||
|
./ghcup-bin set ${GHC_VERSION}
|
||||||
|
./ghcup-bin install-cabal ${CABAL_VERSION}
|
||||||
|
|
||||||
|
exit 0
|
||||||
23
.gitlab/before_script/freebsd/install_deps.sh
Executable file
23
.gitlab/before_script/freebsd/install_deps.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
# pkg install --force --yes --no-repo-update curl gcc gmp gmake ncurses perl5 libffi libiconv
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../../ghcup_env"
|
||||||
|
|
||||||
|
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-portbld-freebsd-ghcup > ./ghcup-bin
|
||||||
|
chmod +x ghcup-bin
|
||||||
|
|
||||||
|
mkdir -p "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin
|
||||||
|
# ./ghcup-bin install ${GHC_VERSION}
|
||||||
|
# ./ghcup-bin install-cabal ${CABAL_VERSION}
|
||||||
|
# ./ghcup-bin set ${GHC_VERSION}
|
||||||
|
|
||||||
|
# install cabal-3.2.0.0
|
||||||
|
curl -sSfL -o cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz 'https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz&dl=1'
|
||||||
|
tar xf cabal-install-3.2.0.0-x86_64-portbld-freebsd.tar.xz
|
||||||
|
cp cabal "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
||||||
|
chmod +x "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
||||||
|
|
||||||
|
exit 0
|
||||||
60
.gitlab/before_script/linux/alpine/install_deps.sh
Executable file
60
.gitlab/before_script/linux/alpine/install_deps.sh
Executable file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../../../ghcup_env"
|
||||||
|
|
||||||
|
apk add --no-cache \
|
||||||
|
curl \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
gmp-dev \
|
||||||
|
ncurses-dev \
|
||||||
|
libffi-dev \
|
||||||
|
make \
|
||||||
|
xz \
|
||||||
|
tar \
|
||||||
|
perl
|
||||||
|
|
||||||
|
ln -s libncurses.so /usr/lib/libtinfo.so
|
||||||
|
ln -s libncursesw.so.6 /usr/lib/libtinfow.so.6
|
||||||
|
if [ "${BIT}" = "32" ] ; then
|
||||||
|
curl -sSfL https://downloads.haskell.org/~ghcup/0.1.4/i386-linux-ghcup-0.1.4 > ./ghcup-bin
|
||||||
|
else
|
||||||
|
curl -sSfL https://downloads.haskell.org/~ghcup/0.1.4/x86_64-linux-ghcup-0.1.4 > ./ghcup-bin
|
||||||
|
fi
|
||||||
|
chmod +x ghcup-bin
|
||||||
|
./ghcup-bin upgrade
|
||||||
|
./ghcup-bin install ${GHC_VERSION}
|
||||||
|
# ./ghcup-bin install-cabal ${CABAL_VERSION}
|
||||||
|
# install cabal-3.2.0.0
|
||||||
|
if [ "${BIT}" = "32" ] ; then
|
||||||
|
curl -sSfL -o cabal-install-3.2.0.0-i386-alpine-linux-musl.tar.xz 'https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-i386-alpine-linux-musl.tar.xz&dl=1'
|
||||||
|
tar xf cabal-install-3.2.0.0-i386-alpine-linux-musl.tar.xz
|
||||||
|
cp cabal-install-3.2.0.0-i386-alpine-linux-musl "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
||||||
|
else
|
||||||
|
curl -sSfL -o cabal-install-3.2.0.0-x86_64-alpine-linux-musl.tar.xz 'https://hasufell.de/d/d3e215db133e4fcaa61e/files/?p=/cabal-install-3.2.0.0-x86_64-alpine-linux-musl.tar.xz&dl=1'
|
||||||
|
tar xf cabal-install-3.2.0.0-x86_64-alpine-linux-musl.tar.xz
|
||||||
|
cp cabal-install-3.2.0.0-x86_64-alpine-linux-musl "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
||||||
|
fi
|
||||||
|
chmod +x "${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin/cabal"
|
||||||
|
|
||||||
|
|
||||||
|
# utils
|
||||||
|
apk add --no-cache \
|
||||||
|
bash
|
||||||
|
|
||||||
|
## Package specific
|
||||||
|
apk add --no-cache \
|
||||||
|
zlib \
|
||||||
|
zlib-dev \
|
||||||
|
zlib-static \
|
||||||
|
gmp \
|
||||||
|
gmp-dev \
|
||||||
|
openssl-dev \
|
||||||
|
openssl-libs-static \
|
||||||
|
xz \
|
||||||
|
xz-dev
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
16
.gitlab/before_script/linux/install_deps.sh
Executable file
16
.gitlab/before_script/linux/install_deps.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install -y libnuma-dev zlib1g-dev libgmp-dev libgmp10 libssl-dev liblzma-dev
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../../ghcup_env"
|
||||||
|
|
||||||
|
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-linux-ghcup > ./ghcup-bin
|
||||||
|
chmod +x ghcup-bin
|
||||||
|
|
||||||
|
./ghcup-bin install ${GHC_VERSION}
|
||||||
|
./ghcup-bin set ${GHC_VERSION}
|
||||||
|
./ghcup-bin install-cabal ${CABAL_VERSION}
|
||||||
|
|
||||||
3
.gitlab/ghcup_env
Normal file
3
.gitlab/ghcup_env
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
|
||||||
|
export PATH="$CI_PROJECT_DIR/.ghcup/bin:$CI_PROJECT_DIR/.local/bin:$PATH"
|
||||||
|
|
||||||
28
.gitlab/script/ghcup_release.sh
Executable file
28
.gitlab/script/ghcup_release.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
|
||||||
|
|
||||||
|
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||||
|
|
||||||
|
ecabal() {
|
||||||
|
cabal --store-dir="$(pwd)"/.store "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# build
|
||||||
|
ecabal update
|
||||||
|
|
||||||
|
if [ "${OS}" = "LINUX" ] ; then
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} -fcurl --ghc-options='-split-sections -optl-static'
|
||||||
|
elif [ "${OS}" = "FREEBSD" ] ; then
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} -fcurl --ghc-options='-split-sections'
|
||||||
|
else
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} -fcurl
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir out
|
||||||
|
cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup')" .
|
||||||
|
ver=$(./ghcup --numeric-version)
|
||||||
|
cp ghcup out/${ARTIFACT}-${ver}
|
||||||
|
|
||||||
87
.gitlab/script/ghcup_version.sh
Executable file
87
.gitlab/script/ghcup_version.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
. "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
|
||||||
|
|
||||||
|
mkdir -p "$CI_PROJECT_DIR"/.local/bin
|
||||||
|
|
||||||
|
ecabal() {
|
||||||
|
cabal --store-dir="$(pwd)"/.store "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
eghcup() {
|
||||||
|
ghcup -v -c -s file://$(pwd)/ghcup-${JSON_VERSION}.json "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### build
|
||||||
|
|
||||||
|
ecabal update
|
||||||
|
|
||||||
|
if [ "${OS}" = "DARWIN" ] ; then
|
||||||
|
ecabal build -w ghc-${GHC_VERSION} -fcurl
|
||||||
|
else
|
||||||
|
ecabal build -w ghc-${GHC_VERSION}
|
||||||
|
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-gen')" .
|
||||||
|
|
||||||
|
cp ./ghcup "$CI_PROJECT_DIR"/.local/bin/ghcup
|
||||||
|
cp ./ghcup-gen "$CI_PROJECT_DIR"/.local/bin/ghcup-gen
|
||||||
|
|
||||||
|
### cleanup
|
||||||
|
|
||||||
|
rm -rf "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup
|
||||||
|
|
||||||
|
|
||||||
|
### manual cli based testing
|
||||||
|
|
||||||
|
|
||||||
|
ghcup-gen check -f ghcup-${JSON_VERSION}.json
|
||||||
|
|
||||||
|
eghcup --numeric-version
|
||||||
|
|
||||||
|
# TODO: rm once we have tarballs
|
||||||
|
if [ "${OS}" = "FREEBSD" ] ; then
|
||||||
|
GHC_VERSION=8.6.3
|
||||||
|
CABAL_VERSION=2.4.1.0
|
||||||
|
fi
|
||||||
|
|
||||||
|
eghcup install ${GHC_VERSION}
|
||||||
|
eghcup set ${GHC_VERSION}
|
||||||
|
eghcup install-cabal ${CABAL_VERSION}
|
||||||
|
|
||||||
|
cabal --version
|
||||||
|
|
||||||
|
eghcup debug-info
|
||||||
|
|
||||||
|
eghcup list
|
||||||
|
eghcup list -t ghc
|
||||||
|
eghcup list -t cabal
|
||||||
|
|
||||||
|
ghc_ver=$(ghc --numeric-version)
|
||||||
|
ghc --version
|
||||||
|
ghci --version
|
||||||
|
ghc-$(ghc --numeric-version) --version
|
||||||
|
ghci-$(ghc --numeric-version) --version
|
||||||
|
|
||||||
|
|
||||||
|
# test installing new ghc doesn't mess with currently set GHC
|
||||||
|
# https://gitlab.haskell.org/haskell/ghcup-hs/issues/7
|
||||||
|
eghcup install 8.4.4
|
||||||
|
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||||
|
eghcup set 8.4.4
|
||||||
|
eghcup set 8.4.4
|
||||||
|
[ "$(ghc --numeric-version)" = "8.4.4" ]
|
||||||
|
eghcup set ${GHC_VERSION}
|
||||||
|
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||||
|
eghcup rm 8.4.4
|
||||||
|
[ "$(ghc --numeric-version)" = "${ghc_ver}" ]
|
||||||
|
|
||||||
|
eghcup rm $(ghc --numeric-version)
|
||||||
|
|
||||||
|
eghcup upgrade
|
||||||
|
eghcup upgrade -f
|
||||||
|
|
||||||
25
.travis.yml
Normal file
25
.travis.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode10.1
|
||||||
|
language: generic
|
||||||
|
env: ARTIFACT=x86_64-apple-darwin-10.13-ghcup
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode11.3
|
||||||
|
language: generic
|
||||||
|
env: ARTIFACT=x86_64-apple-darwin-10.14-ghcup
|
||||||
|
|
||||||
|
script: ".travis/build.sh"
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
provider: releases
|
||||||
|
api_key:
|
||||||
|
secure: GQESg4TcYf3PQJRRaZV/kWS0hsF+OFnH2+EcwpgnIcfx4+aogMyprdh745KtBXe1FlFN1luKHksFjqceqhcg/xcNyeCJiSnLWMn4D/i4WUperEHseRBi5yZZCB1AvOjIlHrE4DS3a8pyEm1GV3G7CKY5Fu8jBjof2SnyENfd7fofhjtNHWmeFS+jBn8HRDf1YaSRYxzTw6uHLrPLsybfgQZVl7babMu/38Ghin0f5pz5OlNokzDxaubIYQHOZ7st7YndHJtBWWql/KualBWbMILy88dUVQBnbqQLP2P8d1ME8ILUjJVqz33HiRU0JzlEJyWfbvEjcJ6iD8M6n4nXTaxfu3i2UhhGsQ6SSBNKssMP4tji8nkNpMqG59wLQ/zhcetEm71fKkgJNrIMNllkqlWSo5K74IqqP9kiLg/qm8ipOJjui0gPk8tZXKcV+ztX1d1OVCapoLfiDM5l/0LLQXaTOXOV1x3e5LLQ/w2doNiH3eh5CV4II9dRu7owpaiiMBHMssmT0pH99jEeF6giHLKtt3y7l2GWoRLPdhsZZ54gxsaBxZt9GuypmkbNcr97CEnAVaWij5v0CF3w4rAWqy/tAxQpIDJOIOQBgmwG5WrBAKyKrFvEpBL5a8BPcRWJDvqKC83QeWpvPrEVdgJevC6ZN1MKzrb2SiPOwC2Kerc=
|
||||||
|
file: $ARTIFACT
|
||||||
|
on:
|
||||||
|
repo: haskell/ghcup-hs
|
||||||
|
tags: true
|
||||||
|
skip_cleanup: true
|
||||||
|
draft: true
|
||||||
|
|
||||||
22
.travis/build.sh
Executable file
22
.travis/build.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#/bin/sh
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
## install ghc via old ghcup
|
||||||
|
|
||||||
|
mkdir -p ~/.ghcup/bin
|
||||||
|
curl https://gitlab.haskell.org/haskell/ghcup/raw/master/ghcup > ~/.ghcup/bin/ghcup
|
||||||
|
chmod +x ~/.ghcup/bin/ghcup
|
||||||
|
|
||||||
|
export PATH="$HOME/.ghcup/bin:$PATH"
|
||||||
|
|
||||||
|
ghcup install 8.8.3
|
||||||
|
ghcup install-cabal 3.2.0.0
|
||||||
|
ghcup set 8.8.3
|
||||||
|
|
||||||
|
|
||||||
|
## install ghcup
|
||||||
|
|
||||||
|
cabal update
|
||||||
|
cabal build -fcurl
|
||||||
|
cp "$(cabal new-exec --verbose=0 --offline sh -- -c 'command -v ghcup')" "./${ARTIFACT}"
|
||||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,5 +1,24 @@
|
|||||||
# Revision history for ghcup
|
# Revision history for ghcup
|
||||||
|
|
||||||
## 0.1.0.0 -- YYYY-mm-dd
|
## 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
|
||||||
|
* Fix unlinking of ghc symlinks after new installation, wrt https://gitlab.haskell.org/haskell/ghcup-hs/issues/7
|
||||||
|
|
||||||
|
## 0.1.3 -- 2020-04-15
|
||||||
|
|
||||||
|
* Fix lesser bug when skipping ghcup update
|
||||||
|
|
||||||
|
## 0.1.2 -- 2020-04-15
|
||||||
|
|
||||||
|
* Fix bug when removing the set GHC version
|
||||||
|
* Fix use of undocumented `GHCUP_INSTALL_BASE_PREFIX` variable
|
||||||
|
* skip upgrade if ghcup is already latest version
|
||||||
|
|
||||||
|
## 0.1.1 -- 2020-04-15
|
||||||
|
|
||||||
|
* fix awful fdopendir bug on mac bug by updating hpath-posix
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
* First version. Released on an unsuspecting world.
|
* First version. Released on an unsuspecting world.
|
||||||
|
|||||||
42
Dockerfile
Normal file
42
Dockerfile
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
FROM alpine:edge
|
||||||
|
|
||||||
|
# ghc and cabal
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
curl \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
gmp-dev \
|
||||||
|
ncurses-dev \
|
||||||
|
libffi-dev \
|
||||||
|
make \
|
||||||
|
xz \
|
||||||
|
tar \
|
||||||
|
perl \
|
||||||
|
\
|
||||||
|
cabal \
|
||||||
|
ghc
|
||||||
|
|
||||||
|
# utils
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
bash
|
||||||
|
|
||||||
|
## Package specific
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
zlib \
|
||||||
|
zlib-dev \
|
||||||
|
zlib-static \
|
||||||
|
gmp \
|
||||||
|
gmp-dev \
|
||||||
|
openssl-dev \
|
||||||
|
openssl-libs-static \
|
||||||
|
xz \
|
||||||
|
xz-dev
|
||||||
|
|
||||||
|
RUN cabal v2-update
|
||||||
|
|
||||||
|
COPY . /app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN chmod +x /app/docker/build.sh
|
||||||
|
|
||||||
45
HACKING.md
Normal file
45
HACKING.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# HACKING
|
||||||
|
|
||||||
|
## Design decisions
|
||||||
|
|
||||||
|
### Using [Excepts](https://hackage.haskell.org/package/haskus-utils-variant-3.0/docs/Haskus-Utils-Variant-Excepts.html) as a beefed up ExceptT
|
||||||
|
|
||||||
|
This is an open variant, similar to [plucky](https://hackage.haskell.org/package/plucky) or [oops](https://github.com/i-am-tom/oops) and allows us to combine different error types. Maybe it is too much and it's a little bit [unergonomic](https://github.com/haskus/packages/issues/32) at times. If it really hurts maintenance, it will be removed. It was more of an experiment.
|
||||||
|
|
||||||
|
### No use of filepath or directory
|
||||||
|
|
||||||
|
Filepath and directory have two fundamental problems: 1. they use String as filepath (see [AFPP](https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/abstract-file-path) as to why this is wrong) and 2. they try very hard to be cross-platform at the expense of low-level correctness. Instead, we use the [hpath](https://github.com/hasufell/hpath) libraries for file and filepath related stuff, which also gives us stronger filepath types.
|
||||||
|
|
||||||
|
### No use of haskell-TLS
|
||||||
|
|
||||||
|
I consider haskell-TLS an interesting experiment, but not a battle-tested and peer-reviewed crypto implementation. There is little to no research about what the intricacies of using haskell for low-level crypto are and how vulnerable such binaries are. Instead, we use either curl the binary (for FreeBSD and mac) or http-io-streams, which works with OpenSSL bindings.
|
||||||
|
|
||||||
|
### Optics instead of lens
|
||||||
|
|
||||||
|
They're a little safer (less Monoid weirdness with view) and have better error messages. Consider the following wit lens
|
||||||
|
|
||||||
|
```
|
||||||
|
> view (_Just . to (++ "abc")) Nothing
|
||||||
|
""
|
||||||
|
```
|
||||||
|
|
||||||
|
vs optics
|
||||||
|
|
||||||
|
```
|
||||||
|
> view (_Just % to (++ "abc")) Nothing
|
||||||
|
|
||||||
|
<interactive>:2:1: error:
|
||||||
|
• An_AffineFold cannot be used as A_Getter
|
||||||
|
• In the expression: view (_Just % to (++ "abc")) Nothing
|
||||||
|
In an equation for ‘it’: it = view (_Just % to (++ "abc")) Nothing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strict and StrictData on by default
|
||||||
|
|
||||||
|
Kazu Yamamoto [explained it in his PR](https://github.com/yesodweb/wai/pull/752#issuecomment-501531386) very well. I like to agree with him. The instances where we need non-strict behavior, we annotate it.
|
||||||
|
|
||||||
|
## Code style and formatting
|
||||||
|
|
||||||
|
1. Brittany
|
||||||
|
2. mtl-style preferred
|
||||||
|
3. no overly pointfree style
|
||||||
166
README.md
166
README.md
@@ -1,37 +1,153 @@
|
|||||||
# ghcup
|
`ghcup` makes it easy to install specific versions of `ghc` on GNU/Linux,
|
||||||
|
macOS (aka Darwin) and FreeBSD and can also bootstrap a fresh Haskell developer environment from scratch.
|
||||||
|
It follows the unix UNIX philosophy of [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well).
|
||||||
|
|
||||||
A rewrite of ghcup in haskell.
|
Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [pyenv](https://github.com/pyenv/pyenv) and [jenv](http://www.jenv.be).
|
||||||
|
|
||||||
## TODO
|
*Ubuntu users may prefer [hvr's ppa](https://launchpad.net/~hvr/+archive/ubuntu/ghc).*
|
||||||
|
|
||||||
* create static ghcup binaries
|
## Table of Contents
|
||||||
* adjust url in GHCupDownloads
|
|
||||||
* add print-system-reqs command
|
|
||||||
|
|
||||||
## Motivation
|
* [Installation](#installation)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Manpages](#manpages)
|
||||||
|
* [Design goals](#design-goals)
|
||||||
|
* [How](#how)
|
||||||
|
* [Known users](#known-users)
|
||||||
|
* [Known problems](#known-problems)
|
||||||
|
* [FAQ](#faq)
|
||||||
|
|
||||||
Maintenance problems:
|
## Installation
|
||||||
|
|
||||||
* platform incompatibilities regularly causing breaking bugs:
|
### Simple bootstrap
|
||||||
* [Mktemp not working properly on macOS](https://gitlab.haskell.org/haskell/ghcup/issues/130)
|
|
||||||
* [ln: illegal option -- T on macOS Catalina](https://gitlab.haskell.org/haskell/ghcup/issues/123)
|
|
||||||
* [Wrong tar flag on darwin](https://gitlab.haskell.org/haskell/ghcup/issues/119))
|
|
||||||
* refactoring being difficult due to POSIX sh
|
|
||||||
|
|
||||||
Benefits of a rewrite:
|
Follow the instructions at [https://www.haskell.org/ghcup/](https://www.haskell.org/ghcup/)
|
||||||
|
|
||||||
* Features such as installing [release candidates](https://gitlab.haskell.org/haskell/ghcup/issues/94) or [HEAD builds](https://gitlab.haskell.org/haskell/ghcup/issues/65) can be more conveniently implemented in a rewrite
|
### Manual install
|
||||||
* Refactoring will be easier
|
|
||||||
* Better tool support (such as linting the downloads file)
|
|
||||||
* saner downloads file format (such as JSON)
|
|
||||||
|
|
||||||
Downsides:
|
Download the binary for your platform at [https://downloads.haskell.org/~ghcup/](https://downloads.haskell.org/~ghcup/)
|
||||||
|
and place it into your `PATH` anywhere.
|
||||||
|
|
||||||
* building static binaries for all platforms (and possibly causing SSL/DNS problems)
|
Then adjust your `PATH` in `~/.bashrc` (or similar, depending on your shell) like so:
|
||||||
* still bootstrapping those binaries via a POSIX sh script
|
|
||||||
|
|
||||||
## Goals
|
```sh
|
||||||
|
export PATH="$HOME/.cabal/bin:$HOME/.ghcup/bin:$PATH"
|
||||||
|
```
|
||||||
|
|
||||||
* Correct low-level code
|
## Usage
|
||||||
* Good exception handling
|
|
||||||
* Cleaner user interface
|
See `ghcup --help`.
|
||||||
|
|
||||||
|
Common use cases are:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# list available ghc/cabal versions
|
||||||
|
ghcup list
|
||||||
|
|
||||||
|
# install the recommended GHC version
|
||||||
|
ghcup install
|
||||||
|
|
||||||
|
# install a specific GHC version
|
||||||
|
ghcup install 8.2.2
|
||||||
|
|
||||||
|
# set the currently "active" GHC version
|
||||||
|
ghcup set 8.4.4
|
||||||
|
|
||||||
|
# install cabal-install
|
||||||
|
ghcup install-cabal
|
||||||
|
|
||||||
|
# update ghcup itself
|
||||||
|
ghcup upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
Generally this is meant to be used with [`cabal-install`](https://hackage.haskell.org/package/cabal-install), which
|
||||||
|
handles your haskell packages and can demand that [a specific version](https://cabal.readthedocs.io/en/latest/nix-local-build.html#cfg-flag---with-compiler) of `ghc` is available, which `ghcup` can do.
|
||||||
|
|
||||||
|
### Manpages
|
||||||
|
|
||||||
|
For man pages to work you need [man-db](http://man-db.nongnu.org/) as your `man` provider, then issue `man ghc`. Manpages only work for the currently set ghc.
|
||||||
|
`MANPATH` may be required to be unset.
|
||||||
|
|
||||||
|
### Bash-completion
|
||||||
|
|
||||||
|
Depending on your distro and setup, install `.bash-completion` from this repo
|
||||||
|
as e.g. `/etc/bash_completion.d/ghcup` and make sure your bashrc sources the
|
||||||
|
startup script (`/usr/share/bash-completion/bash_completion` on some distros).
|
||||||
|
|
||||||
|
## Design goals
|
||||||
|
|
||||||
|
1. simplicity
|
||||||
|
2. non-interactive
|
||||||
|
3. portable (eh)
|
||||||
|
4. do one thing and do it well (UNIX philosophy)
|
||||||
|
|
||||||
|
### Non-goals
|
||||||
|
|
||||||
|
1. invoking `sudo`, `apt-get` or *any* package manager
|
||||||
|
2. handling system packages
|
||||||
|
3. handling cabal projects
|
||||||
|
4. being a stack alternative
|
||||||
|
|
||||||
|
## How
|
||||||
|
|
||||||
|
Installs a specified GHC version into `~/.ghcup/ghc/<ver>`, and places `ghc-<ver>` symlinks in `~/.ghcup/bin/`.
|
||||||
|
|
||||||
|
Optionally, an unversioned `ghc` link can point to a default version of your choice.
|
||||||
|
|
||||||
|
This uses precompiled GHC binaries that have been compiled on fedora/debian by [upstream GHC](https://www.haskell.org/ghc/download_ghc_8_6_1.html#binaries).
|
||||||
|
|
||||||
|
Alternatively, you can also tell it to compile from source (note that this might fail due to missing requirements).
|
||||||
|
|
||||||
|
In addition this script can also install `cabal-install`.
|
||||||
|
|
||||||
|
## Known users
|
||||||
|
|
||||||
|
* [vabal](https://github.com/Franciman/vabal)
|
||||||
|
|
||||||
|
## Known problems
|
||||||
|
|
||||||
|
### Limited distributions supported
|
||||||
|
|
||||||
|
Currently only GNU/Linux distributions compatible with the [upstream GHC](https://www.haskell.org/ghc/download_ghc_8_6_1.html#binaries) binaries are supported.
|
||||||
|
|
||||||
|
### Precompiled binaries
|
||||||
|
|
||||||
|
Since this uses precompiled binaries you may run into
|
||||||
|
several problems.
|
||||||
|
|
||||||
|
#### Missing libtinfo (ncurses)
|
||||||
|
|
||||||
|
You may run into problems with *ncurses* and **missing libtinfo**, in case
|
||||||
|
your distribution doesn't use the legacy way of building
|
||||||
|
ncurses and has no compatibility symlinks in place.
|
||||||
|
|
||||||
|
Ask your distributor on how to solve this or
|
||||||
|
try to compile from source via `ghcup compile <version>`.
|
||||||
|
|
||||||
|
#### Libnuma required
|
||||||
|
|
||||||
|
This was a [bug](https://ghc.haskell.org/trac/ghc/ticket/15688) in the build system of some GHC versions that lead to
|
||||||
|
unconditionally enabled libnuma support. To mitigate this you might have to install the libnuma
|
||||||
|
package of your distribution. See [here](https://gitlab.haskell.org/haskell/ghcup/issues/58) for a discussion.
|
||||||
|
|
||||||
|
### Compilation
|
||||||
|
|
||||||
|
Although this script can compile GHC for you, it's just a very thin
|
||||||
|
wrapper around the build system. It makes no effort in trying
|
||||||
|
to figure out whether you have the correct toolchain and
|
||||||
|
the correct dependencies. Refer to [the official docs](https://ghc.haskell.org/trac/ghc/wiki/Building/Preparation/Linux)
|
||||||
|
on how to prepare your environment for building GHC.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
1. Why reimplement stack?
|
||||||
|
|
||||||
|
ghcup is not a reimplementation of stack. The only common part is automatic installation of GHC, but even that differs in scope and design.
|
||||||
|
|
||||||
|
2. Why not support windows?
|
||||||
|
|
||||||
|
Consider using [Chocolatey](https://chocolatey.org/search?q=ghc) or [ghcups](https://github.com/kakkun61/ghcups).
|
||||||
|
|
||||||
|
3. Why the haskell reimplementation?
|
||||||
|
|
||||||
|
Why not?
|
||||||
|
|||||||
11
RELEASING.md
Normal file
11
RELEASING.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# RELEASING
|
||||||
|
|
||||||
|
1. update `GHCup.Version` module. `ghcupURL` must only be updated if we change the `_toolRequirements` type or the JSON representation of it. The version of the json represents the change increments. `ghcUpVer` is the current application version.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
4. Upload the new `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.
|
||||||
40
TODO.md
Normal file
40
TODO.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# TODOs and Remarks
|
||||||
|
|
||||||
|
## Now
|
||||||
|
|
||||||
|
* try to run exe before upgrade (backup to ~/.ghcup/bin/ghcup.old)
|
||||||
|
* allow to build 8.8
|
||||||
|
* curl DL does not cache json
|
||||||
|
* explain environment variables
|
||||||
|
* add --keep=<always|error> option
|
||||||
|
|
||||||
|
* allow to switch to curl/wget at runtime
|
||||||
|
|
||||||
|
* cross support
|
||||||
|
* installing multiple versions of the same
|
||||||
|
|
||||||
|
* proper test suite
|
||||||
|
* add more logging
|
||||||
|
|
||||||
|
|
||||||
|
## Maybe
|
||||||
|
|
||||||
|
* version ranges in json
|
||||||
|
* sign the JSON? (Or check gpg keys?)
|
||||||
|
* testing (especially distro detection -> unit tests)
|
||||||
|
|
||||||
|
## Later
|
||||||
|
|
||||||
|
* add support for RC/alpha/HEAD versions
|
||||||
|
|
||||||
|
## Cleanups
|
||||||
|
|
||||||
|
* avoid alternative for IO
|
||||||
|
* use plucky or oops instead of Excepts
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
* move out GHCup.Version module, bc it's not library-ish?
|
||||||
|
* mirror support
|
||||||
|
* interactive handling when distro doesn't exist and we know the tarball is incompatible?
|
||||||
|
* ghcup-with wrapper to execute a command with a given ghc in PATH?
|
||||||
@@ -1,20 +1,25 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
|
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
|
import GHCup.Data.GHCupInfo
|
||||||
|
import GHCup.Types
|
||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
import GHCup.Utils.Logger
|
import GHCup.Utils.Logger
|
||||||
import GHCupDownloads
|
|
||||||
|
|
||||||
import Data.Aeson ( eitherDecode )
|
import Data.Aeson ( eitherDecode, encode )
|
||||||
import Data.Aeson.Encode.Pretty
|
import Data.Aeson.Encode.Pretty
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
import Data.Semigroup ( (<>) )
|
import Data.Semigroup ( (<>) )
|
||||||
|
#endif
|
||||||
import Options.Applicative hiding ( style )
|
import Options.Applicative hiding ( style )
|
||||||
import System.Console.Pretty
|
import System.Console.Pretty
|
||||||
import System.Exit
|
import System.Exit
|
||||||
@@ -57,10 +62,13 @@ outputP = fileOutput <|> stdOutput
|
|||||||
|
|
||||||
data GenJSONOpts = GenJSONOpts
|
data GenJSONOpts = GenJSONOpts
|
||||||
{ output :: Maybe Output
|
{ output :: Maybe Output
|
||||||
|
, pretty :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
genJSONOpts :: Parser GenJSONOpts
|
genJSONOpts :: Parser GenJSONOpts
|
||||||
genJSONOpts = GenJSONOpts <$> optional outputP
|
genJSONOpts = GenJSONOpts <$> optional outputP <*> switch
|
||||||
|
(short 'p' <> long "pretty" <> help "Make JSON output pretty (human readable)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
data Input
|
data Input
|
||||||
@@ -130,14 +138,16 @@ main = do
|
|||||||
customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
|
customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
|
||||||
>>= \Options {..} -> case optCommand of
|
>>= \Options {..} -> case optCommand of
|
||||||
GenJSON gopts -> do
|
GenJSON gopts -> do
|
||||||
let
|
let bs True =
|
||||||
bs = encodePretty' (defConfig { confIndent = Spaces 2 })
|
encodePretty' (defConfig { confIndent = Spaces 2 }) ghcupInfo
|
||||||
ghcupDownloads
|
bs False = encode ghcupInfo
|
||||||
case gopts of
|
case gopts of
|
||||||
GenJSONOpts { output = Nothing } -> L.hPutStr stdout bs
|
GenJSONOpts { output = Nothing, pretty } ->
|
||||||
GenJSONOpts { output = Just StdOutput } -> L.hPutStr stdout bs
|
L.hPutStr stdout (bs pretty)
|
||||||
GenJSONOpts { output = Just (FileOutput file) } ->
|
GenJSONOpts { output = Just StdOutput, pretty } ->
|
||||||
L.writeFile file bs
|
L.hPutStr stdout (bs pretty)
|
||||||
|
GenJSONOpts { output = Just (FileOutput file), pretty } ->
|
||||||
|
L.writeFile file (bs pretty)
|
||||||
ValidateJSON vopts -> case vopts of
|
ValidateJSON vopts -> case vopts of
|
||||||
ValidateJSONOpts { input = Nothing } ->
|
ValidateJSONOpts { input = Nothing } ->
|
||||||
L.getContents >>= valAndExit validate
|
L.getContents >>= valAndExit validate
|
||||||
@@ -156,9 +166,8 @@ main = do
|
|||||||
|
|
||||||
where
|
where
|
||||||
valAndExit f contents = do
|
valAndExit f contents = do
|
||||||
av <- case eitherDecode contents of
|
(GHCupInfo _ av) <- case eitherDecode contents of
|
||||||
Right r -> pure r
|
Right r -> pure r
|
||||||
Left e -> die (color Red $ show e)
|
Left e -> die (color Red $ show e)
|
||||||
myLoggerT (LoggerConfig True (B.hPut stdout) (\_ -> pure ())) (f av)
|
myLoggerT (LoggerConfig True (B.hPut stdout) (\_ -> pure ())) (f av)
|
||||||
>>= exitWith
|
>>= exitWith
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -85,7 +89,7 @@ validate dls = do
|
|||||||
[i|FreeBSD missing for #{t} #{v'} #{arch}|]
|
[i|FreeBSD missing for #{t} #{v'} #{arch}|]
|
||||||
|
|
||||||
checkUniqueTags tool = do
|
checkUniqueTags tool = do
|
||||||
let allTags = join $ fmap snd $ availableToolVersions dls tool
|
let allTags = join $ M.elems $ availableToolVersions dls tool
|
||||||
let nonUnique =
|
let nonUnique =
|
||||||
fmap fst
|
fmap fst
|
||||||
. filter (\(_, b) -> not b)
|
. filter (\(_, b) -> not b)
|
||||||
@@ -105,26 +109,40 @@ 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
|
||||||
let allTags = join $ fmap snd $ availableToolVersions dls tool
|
let allTags = join $ M.elems $ availableToolVersions dls tool
|
||||||
forM_ [Latest, Recommended] $ \t -> case elem t allTags of
|
forM_ [Latest, Recommended] $ \t -> case elem t allTags of
|
||||||
False -> do
|
False -> do
|
||||||
lift $ $(logError) [i|Tag #{t} missing from #{tool}|]
|
lift $ $(logError) [i|Tag #{t} missing from #{tool}|]
|
||||||
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 GHCupURL False
|
let settings = Settings True False Never
|
||||||
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
let runLogger = myLoggerT LoggerConfig { lcPrintDebug = True
|
||||||
, colorOutter = B.hPut stderr
|
, colorOutter = B.hPut stderr
|
||||||
, rawOutter = (\_ -> pure ())
|
, rawOutter = (\_ -> pure ())
|
||||||
|
|||||||
1031
app/ghcup/Main.hs
1031
app/ghcup/Main.hs
File diff suppressed because it is too large
Load Diff
202
bootstrap-haskell
Executable file
202
bootstrap-haskell
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# safety subshell to avoid executing anything in case this script is not downloaded properly
|
||||||
|
(
|
||||||
|
|
||||||
|
: "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}"
|
||||||
|
|
||||||
|
die() {
|
||||||
|
(>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1")
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
edo()
|
||||||
|
{
|
||||||
|
"$@" || die "\"$*\" failed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
eghcup() {
|
||||||
|
if [ -z "${BOOTSTRAP_HASKELL_VERBOSE}" ] ; then
|
||||||
|
edo ghcup "$@"
|
||||||
|
else
|
||||||
|
edo ghcup --verbose "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
download_ghcup() {
|
||||||
|
_plat="$(uname -s)"
|
||||||
|
_arch=$(uname -m)
|
||||||
|
|
||||||
|
case "${_plat}" in
|
||||||
|
"linux"|"Linux")
|
||||||
|
case "${_arch}" in
|
||||||
|
x86_64|amd64)
|
||||||
|
_url=https://downloads.haskell.org/~ghcup/0.1.4/x86_64-linux-ghcup-0.1.4
|
||||||
|
;;
|
||||||
|
i*86)
|
||||||
|
_url=https://downloads.haskell.org/~ghcup/0.1.4/i386-linux-ghcup-0.1.4
|
||||||
|
;;
|
||||||
|
*) die "Unknown architecture: ${_arch}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
"FreeBSD"|"freebsd")
|
||||||
|
case "${_arch}" in
|
||||||
|
x86_64|amd64)
|
||||||
|
;;
|
||||||
|
i*86)
|
||||||
|
die "i386 currently not supported!"
|
||||||
|
;;
|
||||||
|
*) die "Unknown architecture: ${_arch}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
_url=https://downloads.haskell.org/~ghcup/0.1.4/x86_64-portbld-freebsd-ghcup-0.1.4
|
||||||
|
;;
|
||||||
|
"Darwin"|"darwin")
|
||||||
|
case "${_arch}" in
|
||||||
|
x86_64|amd64)
|
||||||
|
;;
|
||||||
|
i*86)
|
||||||
|
die "i386 currently not supported!"
|
||||||
|
;;
|
||||||
|
*) die "Unknown architecture: ${_arch}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
_url=https://downloads.haskell.org/~ghcup/0.1.4/x86_64-apple-darwin-ghcup-0.1.4 ;;
|
||||||
|
*) die "Unknown platform: ${_plat}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
edo curl -Lf "${_url}" > "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin/ghcup
|
||||||
|
|
||||||
|
unset _plat _arch _url
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Welcome to Haskell!"
|
||||||
|
echo
|
||||||
|
echo "This will download and install the Glasgow Haskell Compiler (GHC)"
|
||||||
|
echo "and the Cabal build tool."
|
||||||
|
echo
|
||||||
|
echo "ghcup installs only into the following directory, which can be removed anytime:"
|
||||||
|
echo " $GHCUP_INSTALL_BASE_PREFIX/.ghcup"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Press ENTER to proceed"
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "or ctrl-c to abort."
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Note that this script can be re-run at any given time."
|
||||||
|
echo
|
||||||
|
# Wait for user input to continue.
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
read -r answer </dev/tty
|
||||||
|
fi
|
||||||
|
|
||||||
|
edo mkdir -p "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin
|
||||||
|
|
||||||
|
if command -V "ghcup" >/dev/null 2>&1 ; then
|
||||||
|
if [ -z "${BOOTSTRAP_HASKELL_NO_UPGRADE}" ] ; then
|
||||||
|
eghcup upgrade
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
download_ghcup
|
||||||
|
edo chmod +x "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/bin/ghcup
|
||||||
|
|
||||||
|
cat <<-EOF > "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/env || die "Failed to create env file"
|
||||||
|
export PATH="\$HOME/.cabal/bin:\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/bin:\$PATH"
|
||||||
|
EOF
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
edo . "${GHCUP_INSTALL_BASE_PREFIX}"/.ghcup/env
|
||||||
|
eghcup upgrade
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "$(ghcup tool-requirements)"
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Press ENTER to proceed"
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "or ctrl-c to abort."
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Installation may take a while."
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Wait for user input to continue.
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
read -r answer </dev/tty
|
||||||
|
fi
|
||||||
|
|
||||||
|
eghcup --cache install
|
||||||
|
|
||||||
|
eghcup set
|
||||||
|
eghcup --cache install-cabal
|
||||||
|
|
||||||
|
edo cabal new-update
|
||||||
|
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" ""
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Installation done!"
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" ""
|
||||||
|
|
||||||
|
if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then
|
||||||
|
echo "In order to run ghc and cabal, you need to adjust your PATH variable."
|
||||||
|
echo "You may want to source '$GHCUP_INSTALL_BASE_PREFIX/.ghcup/env' in your shell"
|
||||||
|
echo "configuration to do so (e.g. ~/.bashrc)."
|
||||||
|
|
||||||
|
case $SHELL in
|
||||||
|
*/zsh) # login shell is zsh
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.zshrc"
|
||||||
|
MY_SHELL="zsh" ;;
|
||||||
|
*/bash) # login shell is bash
|
||||||
|
if [ -f "$HOME/.bashrc" ] ; then # bashrc is not sourced by default, so assume it isn't if file does not exist
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.bashrc"
|
||||||
|
else
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.bash_profile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
MY_SHELL="bash" ;;
|
||||||
|
*/sh) # login shell is sh, but might be a symlink to bash or zsh
|
||||||
|
if [ -n "${BASH}" ] ; then
|
||||||
|
if [ -f "$HOME/.bashrc" ] ; then # bashrc is not sourced by default, so assume it isn't if file does not exist
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.bashrc"
|
||||||
|
else
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.bash_profile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
MY_SHELL="bash"
|
||||||
|
elif [ -n "${ZSH_VERSION}" ] ; then
|
||||||
|
GHCUP_PROFILE_FILE="$HOME/.zshrc"
|
||||||
|
MY_SHELL="zsh"
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" ""
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "Detected ${MY_SHELL} shell on your system..."
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "If you want ghcup to automatically add the required PATH variable to \"${GHCUP_PROFILE_FILE}\""
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "answer with YES, otherwise with NO and press ENTER."
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" ""
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
read -r next_answer </dev/tty
|
||||||
|
|
||||||
|
case $next_answer in
|
||||||
|
[Yy]*)
|
||||||
|
echo "[ -f \"\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/env\" ] && source \"\${GHCUP_INSTALL_BASE_PREFIX:=\$HOME}/.ghcup/env\"" >> "${GHCUP_PROFILE_FILE}"
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "OK! ${GHCUP_PROFILE_FILE} has been modified. Restart your terminal for the changes to take effect,"
|
||||||
|
printf "\\033[0;35m%s\\033[0m\\n" "or type \"source ${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/env\" to apply them in your current terminal session."
|
||||||
|
exit 0;;
|
||||||
|
[Nn]*)
|
||||||
|
exit 0;;
|
||||||
|
*)
|
||||||
|
echo "Please type YES or NO and press enter.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
# vim: tabstop=4 shiftwidth=4 expandtab
|
||||||
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
packages: ./ghcup.cabal
|
packages: ./ghcup.cabal
|
||||||
|
|
||||||
with-compiler: ghc-8.6.5
|
|
||||||
|
|
||||||
optimization: 2
|
optimization: 2
|
||||||
|
|
||||||
package streamly
|
package streamly
|
||||||
@@ -13,3 +11,6 @@ package ghcup
|
|||||||
package tar-bytestring
|
package tar-bytestring
|
||||||
ghc-options: -O2
|
ghc-options: -O2
|
||||||
|
|
||||||
|
constraints: http-io-streams -brotli
|
||||||
|
|
||||||
|
allow-newer: base
|
||||||
|
|||||||
@@ -1,229 +0,0 @@
|
|||||||
constraints: any.Cabal ==2.4.0.1,
|
|
||||||
any.HsOpenSSL ==0.11.4.17,
|
|
||||||
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -old-locale,
|
|
||||||
any.IfElse ==0.85,
|
|
||||||
any.QuickCheck ==2.13.2,
|
|
||||||
QuickCheck +templatehaskell,
|
|
||||||
any.StateVar ==1.2,
|
|
||||||
any.abstract-deque ==0.3,
|
|
||||||
abstract-deque -usecas,
|
|
||||||
any.aeson ==1.4.6.0,
|
|
||||||
aeson -bytestring-builder -cffi -developer -fast,
|
|
||||||
any.aeson-pretty ==0.8.8,
|
|
||||||
aeson-pretty -lib-only,
|
|
||||||
any.ansi-terminal ==0.10.3,
|
|
||||||
ansi-terminal -example,
|
|
||||||
any.ansi-wl-pprint ==0.6.9,
|
|
||||||
ansi-wl-pprint -example,
|
|
||||||
any.array ==0.5.3.0,
|
|
||||||
any.ascii-string ==1.0.1.4,
|
|
||||||
any.assoc ==1.0.1,
|
|
||||||
any.async ==2.2.2,
|
|
||||||
async -bench,
|
|
||||||
any.atomic-primops ==0.8.3,
|
|
||||||
atomic-primops -debug,
|
|
||||||
any.attoparsec ==0.13.2.3,
|
|
||||||
attoparsec -developer,
|
|
||||||
any.auto-update ==0.1.6,
|
|
||||||
any.base ==4.12.0.0,
|
|
||||||
any.base-compat ==0.11.1,
|
|
||||||
any.base-orphans ==0.8.2,
|
|
||||||
any.base-prelude ==1.3,
|
|
||||||
any.base16-bytestring ==0.1.1.6,
|
|
||||||
any.base64-bytestring ==1.0.0.3,
|
|
||||||
any.bifunctors ==5.5.7,
|
|
||||||
bifunctors +semigroups +tagged,
|
|
||||||
any.binary ==0.8.6.0,
|
|
||||||
any.blaze-builder ==0.4.1.0,
|
|
||||||
any.brotli ==0.0.0.0,
|
|
||||||
any.brotli-streams ==0.0.0.0,
|
|
||||||
any.bytestring ==0.10.8.2,
|
|
||||||
any.bytestring-builder ==0.10.8.2.0,
|
|
||||||
bytestring-builder +bytestring_has_builder,
|
|
||||||
any.bzlib ==0.5.0.5,
|
|
||||||
any.cabal-doctest ==1.0.8,
|
|
||||||
any.case-insensitive ==1.2.1.0,
|
|
||||||
any.cereal ==0.5.8.1,
|
|
||||||
cereal -bytestring-builder,
|
|
||||||
any.clock ==0.8,
|
|
||||||
clock -llvm,
|
|
||||||
any.cmdargs ==0.10.20,
|
|
||||||
cmdargs +quotation -testprog,
|
|
||||||
any.colour ==2.3.5,
|
|
||||||
any.comonad ==5.0.6,
|
|
||||||
comonad +containers +distributive +test-doctests,
|
|
||||||
any.conduit ==1.3.1.2,
|
|
||||||
any.conduit-extra ==1.3.4,
|
|
||||||
any.containers ==0.6.0.1,
|
|
||||||
any.contravariant ==1.5.2,
|
|
||||||
contravariant +semigroups +statevar +tagged,
|
|
||||||
any.data-default-class ==0.1.2.0,
|
|
||||||
any.data-default-instances-base ==0.1.0.1,
|
|
||||||
any.deepseq ==1.4.4.0,
|
|
||||||
any.deferred-folds ==0.9.10.1,
|
|
||||||
any.directory ==1.3.3.0 || ==1.3.6.0,
|
|
||||||
any.distributive ==0.6.1,
|
|
||||||
distributive +semigroups +tagged,
|
|
||||||
any.dlist ==0.8.0.7,
|
|
||||||
any.easy-file ==0.2.2,
|
|
||||||
any.errors ==2.3.0,
|
|
||||||
any.exceptions ==0.10.4,
|
|
||||||
exceptions +transformers-0-4,
|
|
||||||
any.extra ==1.7,
|
|
||||||
any.fast-logger ==3.0.1,
|
|
||||||
any.filepath ==1.4.2.1,
|
|
||||||
any.focus ==1.0.1.3,
|
|
||||||
any.foldl ==1.4.6,
|
|
||||||
any.free ==5.1.3,
|
|
||||||
any.fusion-plugin-types ==0.1.0,
|
|
||||||
any.generics-sop ==0.5.0.0,
|
|
||||||
any.ghc-boot-th ==8.6.5,
|
|
||||||
any.ghc-prim ==0.5.3,
|
|
||||||
any.happy ==1.19.12,
|
|
||||||
happy +small_base,
|
|
||||||
any.hashable ==1.3.0.0,
|
|
||||||
hashable -examples +integer-gmp +sse2 -sse41,
|
|
||||||
any.haskell-src-exts ==1.23.0,
|
|
||||||
any.haskell-src-meta ==0.8.5,
|
|
||||||
any.haskus-utils-data ==1.2,
|
|
||||||
any.haskus-utils-types ==1.5,
|
|
||||||
any.haskus-utils-variant ==3.0,
|
|
||||||
any.heaps ==0.3.6.1,
|
|
||||||
any.hopenssl ==2.2.4,
|
|
||||||
hopenssl -link-libz,
|
|
||||||
any.hpath ==0.11.0,
|
|
||||||
any.hpath-directory ==0.13.2,
|
|
||||||
any.hpath-filepath ==0.10.4,
|
|
||||||
any.hpath-io ==0.13.1,
|
|
||||||
any.hpath-posix ==0.13.1,
|
|
||||||
any.hsc2hs ==0.68.6,
|
|
||||||
hsc2hs -in-ghc-tree,
|
|
||||||
any.http-io-streams ==0.1.2.0,
|
|
||||||
http-io-streams +brotli,
|
|
||||||
any.indexed-profunctors ==0.1,
|
|
||||||
any.integer-gmp ==1.0.2.0,
|
|
||||||
any.integer-logarithms ==1.0.3,
|
|
||||||
integer-logarithms -check-bounds +integer-gmp,
|
|
||||||
any.io-streams ==1.5.1.0,
|
|
||||||
io-streams -nointeractivetests,
|
|
||||||
any.language-bash ==0.9.0,
|
|
||||||
any.lifted-base ==0.2.3.12,
|
|
||||||
any.list-t ==1.0.4,
|
|
||||||
any.lockfree-queue ==0.2.3.1,
|
|
||||||
any.lzma ==0.0.0.3,
|
|
||||||
any.math-functions ==0.3.3.0,
|
|
||||||
math-functions +system-erf +system-expm1,
|
|
||||||
any.megaparsec ==8.0.0,
|
|
||||||
megaparsec -dev,
|
|
||||||
any.mmorph ==1.1.3,
|
|
||||||
any.monad-control ==1.0.2.3,
|
|
||||||
any.monad-logger ==0.3.32,
|
|
||||||
monad-logger +template_haskell,
|
|
||||||
any.monad-loops ==0.4.3,
|
|
||||||
monad-loops +base4,
|
|
||||||
any.mono-traversable ==1.0.15.1,
|
|
||||||
any.mtl ==2.2.2,
|
|
||||||
any.mwc-random ==0.14.0.0,
|
|
||||||
any.network ==3.1.1.1,
|
|
||||||
any.network-uri ==2.6.3.0,
|
|
||||||
any.old-locale ==1.0.0.7,
|
|
||||||
any.old-time ==1.1.0.3,
|
|
||||||
any.openssl-streams ==1.2.2.0,
|
|
||||||
any.optics ==0.2,
|
|
||||||
any.optics-core ==0.2,
|
|
||||||
any.optics-extra ==0.2,
|
|
||||||
any.optics-th ==0.2,
|
|
||||||
any.optics-vl ==0.2,
|
|
||||||
any.optparse-applicative ==0.15.1.0,
|
|
||||||
any.parsec ==3.1.13.0,
|
|
||||||
any.parser-combinators ==1.2.1,
|
|
||||||
parser-combinators -dev,
|
|
||||||
any.pretty ==1.1.3.6,
|
|
||||||
any.pretty-terminal ==0.1.0.0,
|
|
||||||
any.prettyprinter ==1.6.1,
|
|
||||||
prettyprinter -buildreadme,
|
|
||||||
any.primitive ==0.7.0.1,
|
|
||||||
any.primitive-extras ==0.8,
|
|
||||||
any.primitive-unlifted ==0.1.3.0,
|
|
||||||
any.process ==1.6.5.0 || ==1.6.8.0,
|
|
||||||
any.profunctors ==5.5.2,
|
|
||||||
any.random ==1.1,
|
|
||||||
any.recursion-schemes ==5.1.3,
|
|
||||||
recursion-schemes +template-haskell,
|
|
||||||
any.resourcet ==1.2.3,
|
|
||||||
any.rts ==1.0,
|
|
||||||
any.safe ==0.3.18,
|
|
||||||
any.safe-exceptions ==0.1.7.0,
|
|
||||||
any.scientific ==0.3.6.2,
|
|
||||||
scientific -bytestring-builder -integer-simple,
|
|
||||||
any.semigroupoids ==5.3.4,
|
|
||||||
semigroupoids +comonad +containers +contravariant +distributive +doctests +tagged +unordered-containers,
|
|
||||||
any.semigroups ==0.19.1,
|
|
||||||
semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers,
|
|
||||||
any.sop-core ==0.5.0.0,
|
|
||||||
any.split ==0.2.3.4,
|
|
||||||
any.splitmix ==0.0.4,
|
|
||||||
splitmix -optimised-mixer +random,
|
|
||||||
any.stm ==2.5.0.0,
|
|
||||||
any.stm-chans ==3.0.0.4,
|
|
||||||
any.streaming-commons ==0.2.1.2,
|
|
||||||
streaming-commons -use-bytestring-builder,
|
|
||||||
any.streamly ==0.7.1,
|
|
||||||
streamly -debug -dev -examples -examples-sdl -fusion-plugin -has-llvm -inspection -no-charts -no-fusion -streamk,
|
|
||||||
any.streamly-bytestring ==0.1.2,
|
|
||||||
any.streamly-posix ==0.1.0.0,
|
|
||||||
any.strict-base ==0.4.0.0,
|
|
||||||
any.string-interpolate ==0.2.0.0,
|
|
||||||
any.syb ==0.7.1,
|
|
||||||
any.table-layout ==0.8.0.5,
|
|
||||||
any.tagged ==0.8.6,
|
|
||||||
tagged +deepseq +transformers,
|
|
||||||
any.tar-bytestring ==0.6.3.0,
|
|
||||||
any.template-haskell ==2.14.0.0,
|
|
||||||
any.terminal-progress-bar ==0.4.1,
|
|
||||||
any.terminal-size ==0.3.2.1,
|
|
||||||
any.text ==1.2.3.1,
|
|
||||||
any.text-conversions ==0.3.0,
|
|
||||||
any.text-icu ==0.7.0.1,
|
|
||||||
any.text-short ==0.1.3,
|
|
||||||
text-short -asserts,
|
|
||||||
any.th-abstraction ==0.3.2.0,
|
|
||||||
any.th-expand-syns ==0.4.5.0,
|
|
||||||
any.th-lift ==0.8.1,
|
|
||||||
any.th-lift-instances ==0.1.14,
|
|
||||||
any.th-orphans ==0.13.9,
|
|
||||||
any.th-reify-many ==0.1.9,
|
|
||||||
any.these ==1.0.1,
|
|
||||||
these +aeson +assoc +quickcheck +semigroupoids,
|
|
||||||
any.time ==1.8.0.2 || ==1.9.3,
|
|
||||||
any.time-compat ==1.9.2.2,
|
|
||||||
time-compat -old-locale,
|
|
||||||
any.transformers ==0.5.6.2,
|
|
||||||
any.transformers-base ==0.4.5.2,
|
|
||||||
transformers-base +orphaninstances,
|
|
||||||
any.transformers-compat ==0.6.5,
|
|
||||||
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
|
|
||||||
any.typed-process ==0.2.6.0,
|
|
||||||
any.unix ==2.7.2.2,
|
|
||||||
any.unix-bytestring ==0.3.7.3,
|
|
||||||
any.unix-compat ==0.5.2,
|
|
||||||
unix-compat -old-time,
|
|
||||||
any.unix-time ==0.4.7,
|
|
||||||
any.unliftio-core ==0.2.0.1,
|
|
||||||
any.unordered-containers ==0.2.10.0,
|
|
||||||
unordered-containers -debug,
|
|
||||||
any.uri-bytestring ==0.3.2.2,
|
|
||||||
uri-bytestring -lib-werror,
|
|
||||||
any.utf8-string ==1.0.1.1,
|
|
||||||
any.uuid-types ==1.0.3,
|
|
||||||
any.vector ==0.12.1.2,
|
|
||||||
vector +boundschecks -internalchecks -unsafechecks -wall,
|
|
||||||
any.vector-algorithms ==0.8.0.3,
|
|
||||||
vector-algorithms +bench +boundschecks -internalchecks -llvm +properties -unsafechecks,
|
|
||||||
any.vector-builder ==0.3.8,
|
|
||||||
any.vector-th-unbox ==0.2.1.7,
|
|
||||||
any.versions ==3.5.3,
|
|
||||||
any.word8 ==0.1.3,
|
|
||||||
any.zlib ==0.6.2.1,
|
|
||||||
zlib -non-blocking-ffi -pkg-config,
|
|
||||||
any.zlib-bindings ==0.1.1.5
|
|
||||||
14
docker/build.sh
Normal file
14
docker/build.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd /app
|
||||||
|
|
||||||
|
cabal v2-update
|
||||||
|
|
||||||
|
cabal v2-install \
|
||||||
|
--install-method=copy \
|
||||||
|
--overwrite-policy=always \
|
||||||
|
--installdir="/bin" \
|
||||||
|
--ghc-options='-optl-static'
|
||||||
|
|
||||||
2179
ghcup-0.0.1.json
Normal file
2179
ghcup-0.0.1.json
Normal file
File diff suppressed because it is too large
Load Diff
2251
ghcup-0.0.2.json
Normal file
2251
ghcup-0.0.2.json
Normal file
File diff suppressed because it is too large
Load Diff
582
ghcup.cabal
582
ghcup.cabal
@@ -1,234 +1,388 @@
|
|||||||
cabal-version: 2.2
|
cabal-version: 3.0
|
||||||
|
name: ghcup
|
||||||
|
version: 0.1.4
|
||||||
|
synopsis: ghc toolchain installer as an exe/library
|
||||||
|
description:
|
||||||
|
A rewrite of the shell script ghcup, for providing
|
||||||
|
a more stable user experience and exposing an API.
|
||||||
|
|
||||||
name: ghcup
|
homepage: https://gitlab.haskell.org/haskell/ghcup-hs
|
||||||
version: 0.1.0.0
|
bug-reports: https://gitlab.haskell.org/haskell/ghcup-hs/issues
|
||||||
synopsis: ghc toolchain installer as an exe/library
|
license: LGPL-3.0-only
|
||||||
description: A rewrite of the shell script ghcup, for providing
|
license-file: LICENSE
|
||||||
a more stable user experience and exposing an API.
|
author: Julian Ospald
|
||||||
homepage: https://github.com/hasufell/ghcup-hs
|
maintainer: hasufell@posteo.de
|
||||||
bug-reports: https://github.com/hasufell/ghcup-hs/issues
|
copyright: Julian Ospald 2020
|
||||||
license: LGPL-3.0-only
|
category: System
|
||||||
license-file: LICENSE
|
build-type: Simple
|
||||||
author: Julian Ospald
|
extra-source-files: CHANGELOG.md
|
||||||
maintainer: hasufell@posteo.de
|
|
||||||
copyright: Julian Ospald 2020
|
|
||||||
category: System
|
|
||||||
build-type: Simple
|
|
||||||
extra-source-files: CHANGELOG.md
|
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
location: https://github.com/hasufell/ghcup-hs
|
location: https://gitlab.haskell.org/haskell/ghcup-hs.git
|
||||||
|
|
||||||
common HsOpenSSL { build-depends: HsOpenSSL >= 0.11 }
|
flag Curl
|
||||||
common aeson { build-depends: aeson >= 1.4 }
|
description: Use curl instead of http-io-streams for download
|
||||||
common aeson-pretty { build-depends: aeson-pretty >= 0.8.8 }
|
default: False
|
||||||
common ascii-string { build-depends: ascii-string >= 1.0 }
|
manual: True
|
||||||
common async { build-depends: async >= 0.8 }
|
|
||||||
common attoparsec { build-depends: attoparsec >= 0.13 }
|
|
||||||
common base { build-depends: base >= 4.12 && < 5 }
|
|
||||||
common binary { build-depends: binary >= 0.8.6.0 }
|
|
||||||
common bytestring { build-depends: bytestring >= 0.10 }
|
|
||||||
common bzlib { build-depends: bzlib >= 0.5.0.5 }
|
|
||||||
common case-insensitive { build-depends: case-insensitive >= 1.2.1.0 }
|
|
||||||
common containers { build-depends: containers >= 0.6 }
|
|
||||||
common generics-sop { build-depends: generics-sop >= 0.5 }
|
|
||||||
common haskus-utils-types { build-depends: haskus-utils-types >= 1.5 }
|
|
||||||
common haskus-utils-variant { build-depends: haskus-utils-variant >= 3.0 }
|
|
||||||
common hopenssl { build-depends: hopenssl >= 2.2.4 }
|
|
||||||
common hpath { build-depends: hpath >= 0.11 }
|
|
||||||
common hpath-directory { build-depends: hpath-directory >= 0.13.2 }
|
|
||||||
common hpath-filepath { build-depends: hpath-filepath >= 0.10.3 }
|
|
||||||
common hpath-io { build-depends: hpath-io >= 0.13.1 }
|
|
||||||
common hpath-posix { build-depends: hpath-posix >= 0.11.1 }
|
|
||||||
common http-io-streams { build-depends: http-io-streams >= 0.1.2.0 }
|
|
||||||
common io-streams { build-depends: io-streams >= 1.5 }
|
|
||||||
common language-bash { build-depends: language-bash >= 0.9 }
|
|
||||||
common lzma { build-depends: lzma >= 0.0.0.3 }
|
|
||||||
common monad-logger { build-depends: monad-logger >= 0.3.31 }
|
|
||||||
common mtl { build-depends: mtl >= 2.2 }
|
|
||||||
common optics { build-depends: optics >= 0.2 }
|
|
||||||
common optics-vl { build-depends: optics-vl >= 0.2 }
|
|
||||||
common optparse-applicative { build-depends: optparse-applicative >= 0.15.1.0 }
|
|
||||||
common parsec { build-depends: parsec >= 3.1 }
|
|
||||||
common pretty-terminal { build-depends: pretty-terminal >= 0.1.0.0 }
|
|
||||||
common resourcet { build-depends: resourcet >= 1.2.2 }
|
|
||||||
common safe { build-depends: safe >= 0.3.18 }
|
|
||||||
common safe-exceptions { build-depends: safe-exceptions >= 0.1 }
|
|
||||||
common streamly { build-depends: streamly >= 0.7.1 }
|
|
||||||
common streamly-posix { build-depends: streamly-posix >= 0.1.0.0 }
|
|
||||||
common streamly-bytestring { build-depends: streamly-bytestring >= 0.1.2 }
|
|
||||||
common strict-base { build-depends: strict-base >= 0.4 }
|
|
||||||
common string-interpolate { build-depends: string-interpolate >= 0.2.0.0 }
|
|
||||||
common table-layout { build-depends: table-layout >= 0.8 }
|
|
||||||
common tar-bytestring { build-depends: tar-bytestring >= 0.6.3.0 }
|
|
||||||
common template-haskell { build-depends: template-haskell >= 2.7 }
|
|
||||||
common terminal-progress-bar { build-depends: terminal-progress-bar >= 0.4.1 }
|
|
||||||
common text { build-depends: text >= 1.2 }
|
|
||||||
common text-icu { build-depends: text-icu >= 0.7 }
|
|
||||||
common time { build-depends: time >= 1.9.3 }
|
|
||||||
common transformers { build-depends: transformers >= 0.5 }
|
|
||||||
common unix { build-depends: unix >= 2.7 }
|
|
||||||
common unix-bytestring { build-depends: unix-bytestring >= 0.3 }
|
|
||||||
common uri-bytestring { build-depends: uri-bytestring >= 0.3.2.2 }
|
|
||||||
common utf8-string { build-depends: utf8-string >= 1.0 }
|
|
||||||
common vector { build-depends: vector >= 0.12 }
|
|
||||||
common versions { build-depends: versions >= 3.5 }
|
|
||||||
common waargonaut { build-depends: waargonaut >= 0.8 }
|
|
||||||
common word8 { build-depends: word8 >= 0.1.3 }
|
|
||||||
common zlib { build-depends: zlib >= 0.6.2.1 }
|
|
||||||
|
|
||||||
|
common HsOpenSSL
|
||||||
|
build-depends: HsOpenSSL >=0.11.4.18
|
||||||
|
|
||||||
|
common aeson
|
||||||
|
build-depends: aeson >=1.4
|
||||||
|
|
||||||
|
common aeson-pretty
|
||||||
|
build-depends: aeson-pretty >=0.8.8
|
||||||
|
|
||||||
|
common ascii-string
|
||||||
|
build-depends: ascii-string >=1.0
|
||||||
|
|
||||||
|
common async
|
||||||
|
build-depends: async >=0.8
|
||||||
|
|
||||||
|
common base
|
||||||
|
build-depends: base >=4.12 && <5
|
||||||
|
|
||||||
|
common base16-bytestring
|
||||||
|
build-depends: base16-bytestring >= 0.1.1.6
|
||||||
|
|
||||||
|
common binary
|
||||||
|
build-depends: binary >=0.8.6.0
|
||||||
|
|
||||||
|
common bytestring
|
||||||
|
build-depends: bytestring >=0.10
|
||||||
|
|
||||||
|
common bz2
|
||||||
|
build-depends: bz2 >=0.5.0.5
|
||||||
|
|
||||||
|
common case-insensitive
|
||||||
|
build-depends: case-insensitive >=1.2.1.0
|
||||||
|
|
||||||
|
common concurrent-output
|
||||||
|
build-depends: concurrent-output >=1.10.11
|
||||||
|
|
||||||
|
common containers
|
||||||
|
build-depends: containers >=0.6
|
||||||
|
|
||||||
|
common cryptohash-sha256
|
||||||
|
build-depends: cryptohash-sha256 >= 0.11.101.0
|
||||||
|
|
||||||
|
common generics-sop
|
||||||
|
build-depends: generics-sop >=0.5
|
||||||
|
|
||||||
|
common haskus-utils-types
|
||||||
|
build-depends: haskus-utils-types >=1.5
|
||||||
|
|
||||||
|
common haskus-utils-variant
|
||||||
|
build-depends: haskus-utils-variant >=3.0
|
||||||
|
|
||||||
|
common hpath
|
||||||
|
build-depends: hpath >=0.11
|
||||||
|
|
||||||
|
common hpath-directory
|
||||||
|
build-depends: hpath-directory >=0.13.3
|
||||||
|
|
||||||
|
common hpath-filepath
|
||||||
|
build-depends: hpath-filepath >=0.10.3
|
||||||
|
|
||||||
|
common hpath-io
|
||||||
|
build-depends: hpath-io >=0.13.1
|
||||||
|
|
||||||
|
common hpath-posix
|
||||||
|
build-depends: hpath-posix >=0.13.2
|
||||||
|
|
||||||
|
common http-io-streams
|
||||||
|
build-depends: http-io-streams >=0.1.2.0
|
||||||
|
|
||||||
|
common io-streams
|
||||||
|
build-depends: io-streams >=1.5
|
||||||
|
|
||||||
|
common language-bash
|
||||||
|
build-depends: language-bash >=0.9
|
||||||
|
|
||||||
|
common lzma
|
||||||
|
build-depends: lzma >=0.0.0.3
|
||||||
|
|
||||||
|
common megaparsec
|
||||||
|
build-depends: megaparsec >=8.0.0
|
||||||
|
|
||||||
|
common monad-logger
|
||||||
|
build-depends: monad-logger >=0.3.31
|
||||||
|
|
||||||
|
common mtl
|
||||||
|
build-depends: mtl >=2.2
|
||||||
|
|
||||||
|
common optics
|
||||||
|
build-depends: optics >=0.2
|
||||||
|
|
||||||
|
common optics-vl
|
||||||
|
build-depends: optics-vl >=0.2
|
||||||
|
|
||||||
|
common optparse-applicative
|
||||||
|
build-depends: optparse-applicative >=0.15.1.0
|
||||||
|
|
||||||
|
common parsec
|
||||||
|
build-depends: parsec >=3.1
|
||||||
|
|
||||||
|
common pretty-terminal
|
||||||
|
build-depends: pretty-terminal >=0.1.0.0
|
||||||
|
|
||||||
|
common regex-posix
|
||||||
|
build-depends: regex-posix >=0.96
|
||||||
|
|
||||||
|
common resourcet
|
||||||
|
build-depends: resourcet >=1.2.2
|
||||||
|
|
||||||
|
common safe
|
||||||
|
build-depends: safe >=0.3.18
|
||||||
|
|
||||||
|
common safe-exceptions
|
||||||
|
build-depends: safe-exceptions >=0.1
|
||||||
|
|
||||||
|
common streamly
|
||||||
|
build-depends: streamly >=0.7.1
|
||||||
|
|
||||||
|
common streamly-posix
|
||||||
|
build-depends: streamly-posix >=0.1.0.0
|
||||||
|
|
||||||
|
common streamly-bytestring
|
||||||
|
build-depends: streamly-bytestring >=0.1.2
|
||||||
|
|
||||||
|
common strict-base
|
||||||
|
build-depends: strict-base >=0.4
|
||||||
|
|
||||||
|
common string-interpolate
|
||||||
|
build-depends: string-interpolate >=0.2.0.0
|
||||||
|
|
||||||
|
common table-layout
|
||||||
|
build-depends: table-layout >=0.8
|
||||||
|
|
||||||
|
common tar-bytestring
|
||||||
|
build-depends: tar-bytestring >=0.6.3.1
|
||||||
|
|
||||||
|
common template-haskell
|
||||||
|
build-depends: template-haskell >=2.7
|
||||||
|
|
||||||
|
common terminal-progress-bar
|
||||||
|
build-depends: terminal-progress-bar >=0.4.1
|
||||||
|
|
||||||
|
common text
|
||||||
|
build-depends: text >=1.2
|
||||||
|
|
||||||
|
common time
|
||||||
|
build-depends: time >=1.9.3
|
||||||
|
|
||||||
|
common transformers
|
||||||
|
build-depends: transformers >=0.5
|
||||||
|
|
||||||
|
common unix
|
||||||
|
build-depends: unix >=2.7
|
||||||
|
|
||||||
|
common unix-bytestring
|
||||||
|
build-depends: unix-bytestring >=0.3
|
||||||
|
|
||||||
|
common uri-bytestring
|
||||||
|
build-depends: uri-bytestring >=0.3.2.2
|
||||||
|
|
||||||
|
common utf8-string
|
||||||
|
build-depends: utf8-string >=1.0
|
||||||
|
|
||||||
|
common vector
|
||||||
|
build-depends: vector >=0.12
|
||||||
|
|
||||||
|
common versions
|
||||||
|
build-depends: versions >=3.5
|
||||||
|
|
||||||
|
common waargonaut
|
||||||
|
build-depends: waargonaut >=0.8
|
||||||
|
|
||||||
|
common word8
|
||||||
|
build-depends: word8 >=0.1.3
|
||||||
|
|
||||||
|
common zlib
|
||||||
|
build-depends: zlib >=0.6.2.1
|
||||||
|
|
||||||
common config
|
common config
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -threaded
|
ghc-options:
|
||||||
default-extensions: LambdaCase
|
-Wall -fwarn-tabs -fwarn-incomplete-uni-patterns
|
||||||
, MultiWayIf
|
-fwarn-incomplete-record-updates -threaded
|
||||||
, PackageImports
|
|
||||||
, RecordWildCards
|
default-extensions:
|
||||||
, ScopedTypeVariables
|
LambdaCase
|
||||||
, StrictData
|
MultiWayIf
|
||||||
, Strict
|
PackageImports
|
||||||
, TupleSections
|
RecordWildCards
|
||||||
|
ScopedTypeVariables
|
||||||
|
Strict
|
||||||
|
StrictData
|
||||||
|
TupleSections
|
||||||
|
|
||||||
library
|
library
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
-- deps
|
, base
|
||||||
, HsOpenSSL
|
, base16-bytestring
|
||||||
, aeson
|
, aeson
|
||||||
, ascii-string
|
, ascii-string
|
||||||
, async
|
, async
|
||||||
, attoparsec
|
, binary
|
||||||
, binary
|
, bytestring
|
||||||
, bytestring
|
, bz2
|
||||||
, bzlib
|
, case-insensitive
|
||||||
, case-insensitive
|
, concurrent-output
|
||||||
, containers
|
, containers
|
||||||
, generics-sop
|
, cryptohash-sha256
|
||||||
, haskus-utils-types
|
, generics-sop
|
||||||
, haskus-utils-variant
|
, haskus-utils-types
|
||||||
, hopenssl
|
, haskus-utils-variant
|
||||||
, hpath
|
, hpath
|
||||||
, hpath-directory
|
, hpath-directory
|
||||||
, hpath-filepath
|
, hpath-filepath
|
||||||
, hpath-io
|
, hpath-io
|
||||||
, hpath-posix
|
, hpath-posix
|
||||||
, http-io-streams
|
, language-bash
|
||||||
, io-streams
|
, lzma
|
||||||
, language-bash
|
, megaparsec
|
||||||
, lzma
|
, monad-logger
|
||||||
, monad-logger
|
, mtl
|
||||||
, mtl
|
, optics
|
||||||
, optics
|
, optics-vl
|
||||||
, optics-vl
|
, parsec
|
||||||
, parsec
|
, pretty-terminal
|
||||||
, pretty-terminal
|
, regex-posix
|
||||||
, resourcet
|
, resourcet
|
||||||
, safe
|
, safe
|
||||||
, safe-exceptions
|
, safe-exceptions
|
||||||
, streamly
|
, streamly
|
||||||
, streamly-posix
|
, streamly-posix
|
||||||
, streamly-bytestring
|
, streamly-bytestring
|
||||||
, strict-base
|
, strict-base
|
||||||
, string-interpolate
|
, string-interpolate
|
||||||
, tar-bytestring
|
, tar-bytestring
|
||||||
, template-haskell
|
, template-haskell
|
||||||
, terminal-progress-bar
|
, text
|
||||||
, text
|
, time
|
||||||
, text-icu
|
, transformers
|
||||||
, time
|
, unix
|
||||||
, transformers
|
, unix-bytestring
|
||||||
, unix
|
, uri-bytestring
|
||||||
, unix-bytestring
|
, utf8-string
|
||||||
, uri-bytestring
|
, vector
|
||||||
, utf8-string
|
, versions
|
||||||
, vector
|
, word8
|
||||||
, versions
|
, zlib
|
||||||
, word8
|
|
||||||
, zlib
|
exposed-modules:
|
||||||
exposed-modules: GHCup
|
GHCup
|
||||||
GHCup.Download
|
GHCup.Data.GHCupDownloads
|
||||||
GHCup.Errors
|
GHCup.Data.GHCupInfo
|
||||||
GHCup.Platform
|
GHCup.Data.ToolRequirements
|
||||||
GHCup.Types
|
GHCup.Download
|
||||||
GHCup.Types.JSON
|
GHCup.Download.Utils
|
||||||
GHCup.Types.Optics
|
GHCup.Errors
|
||||||
GHCup.Utils
|
GHCup.Platform
|
||||||
GHCup.Utils.Bash
|
GHCup.Requirements
|
||||||
GHCup.Utils.Dirs
|
GHCup.Types
|
||||||
GHCup.Utils.File
|
GHCup.Types.JSON
|
||||||
GHCup.Utils.Logger
|
GHCup.Types.Optics
|
||||||
GHCup.Utils.Prelude
|
GHCup.Utils
|
||||||
GHCup.Utils.String.QQ
|
GHCup.Utils.Bash
|
||||||
GHCup.Utils.Version.QQ
|
GHCup.Utils.Dirs
|
||||||
GHCup.Version
|
GHCup.Utils.File
|
||||||
|
GHCup.Utils.Logger
|
||||||
|
GHCup.Utils.MegaParsec
|
||||||
|
GHCup.Utils.Prelude
|
||||||
|
GHCup.Utils.String.QQ
|
||||||
|
GHCup.Utils.Version.QQ
|
||||||
|
GHCup.Version
|
||||||
|
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
hs-source-dirs: lib
|
hs-source-dirs: lib
|
||||||
|
|
||||||
|
if !flag(curl)
|
||||||
|
import:
|
||||||
|
, HsOpenSSL
|
||||||
|
, http-io-streams
|
||||||
|
, io-streams
|
||||||
|
, terminal-progress-bar
|
||||||
|
exposed-modules: GHCup.Download.IOStreams
|
||||||
|
else
|
||||||
|
cpp-options: -DCURL
|
||||||
|
|
||||||
executable ghcup
|
executable ghcup
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
--
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, haskus-utils-variant
|
, haskus-utils-variant
|
||||||
, monad-logger
|
, hpath
|
||||||
, mtl
|
, hpath-io
|
||||||
, optparse-applicative
|
, megaparsec
|
||||||
, text
|
, monad-logger
|
||||||
, versions
|
, mtl
|
||||||
, hpath
|
, optparse-applicative
|
||||||
, hpath-io
|
, pretty-terminal
|
||||||
, pretty-terminal
|
, resourcet
|
||||||
, resourcet
|
, safe
|
||||||
, string-interpolate
|
, string-interpolate
|
||||||
, table-layout
|
, table-layout
|
||||||
, uri-bytestring
|
, template-haskell
|
||||||
, utf8-string
|
, text
|
||||||
main-is: Main.hs
|
, uri-bytestring
|
||||||
|
, utf8-string
|
||||||
|
, versions
|
||||||
|
|
||||||
|
--
|
||||||
|
main-is: Main.hs
|
||||||
|
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: ghcup
|
build-depends: ghcup
|
||||||
hs-source-dirs: app/ghcup
|
hs-source-dirs: app/ghcup
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
executable ghcup-gen
|
executable ghcup-gen
|
||||||
import: config
|
import:
|
||||||
, base
|
config
|
||||||
--
|
, base
|
||||||
, aeson
|
, aeson
|
||||||
, aeson-pretty
|
, aeson-pretty
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
, safe-exceptions
|
, haskus-utils-variant
|
||||||
, haskus-utils-variant
|
, hpath
|
||||||
, monad-logger
|
, monad-logger
|
||||||
, mtl
|
, mtl
|
||||||
, optics
|
, optics
|
||||||
, optparse-applicative
|
, optparse-applicative
|
||||||
, text
|
, pretty-terminal
|
||||||
, versions
|
, resourcet
|
||||||
, hpath
|
, safe-exceptions
|
||||||
, pretty-terminal
|
, string-interpolate
|
||||||
, resourcet
|
, table-layout
|
||||||
, string-interpolate
|
, text
|
||||||
, table-layout
|
, transformers
|
||||||
, transformers
|
, uri-bytestring
|
||||||
, uri-bytestring
|
, utf8-string
|
||||||
, utf8-string
|
, versions
|
||||||
main-is: Main.hs
|
|
||||||
other-modules: GHCupDownloads
|
--
|
||||||
Validate
|
main-is: Main.hs
|
||||||
|
other-modules:
|
||||||
|
Validate
|
||||||
|
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: ghcup
|
build-depends: ghcup
|
||||||
hs-source-dirs: app/ghcup-gen
|
hs-source-dirs: app/ghcup-gen
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
test-suite ghcup-test
|
test-suite ghcup-test
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: MyLibTest.hs
|
main-is: MyLibTest.hs
|
||||||
build-depends: base ^>=4.12.0.0
|
build-depends: base >=4.12.0.0
|
||||||
|
|||||||
509
lib/GHCup.hs
509
lib/GHCup.hs
@@ -1,12 +1,15 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
module GHCup where
|
module GHCup where
|
||||||
|
|
||||||
@@ -27,16 +30,18 @@ import GHCup.Version
|
|||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
import Control.Monad.Fail ( MonadFail )
|
import Control.Monad.Fail ( MonadFail )
|
||||||
|
#endif
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Class ( lift )
|
|
||||||
import Control.Monad.Trans.Resource
|
import Control.Monad.Trans.Resource
|
||||||
hiding ( throwM )
|
hiding ( throwM )
|
||||||
import Data.ByteString ( ByteString )
|
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
|
||||||
@@ -49,12 +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.RawFilePath.Directory.Errors
|
import System.Posix.Files.ByteString
|
||||||
( hideError )
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
@@ -91,57 +98,47 @@ 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
|
||||||
|
|
||||||
-- download (or use cached version)
|
-- download (or use cached version)
|
||||||
dlinfo <- liftE $ getDownloadInfo bDls GHC ver mpfReq
|
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
|
||||||
|
|
||||||
-- 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 archiveSubdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
let workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
catchAllE
|
liftE $ runBuildAction tmpUnpack (Just ghcdir) (installGHC' workdir ghcdir)
|
||||||
(\es ->
|
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
|
||||||
>> throwE (BuildFailed archiveSubdir es)
|
|
||||||
)
|
|
||||||
$ installGHC' archiveSubdir 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
|
||||||
-> Excepts '[ProcessError] m ()
|
-> Excepts '[ProcessError] m ()
|
||||||
installGHC' path inst = do
|
installGHC' path inst = do
|
||||||
lift $ $(logInfo) [s|Installing GHC (this may take a while)|]
|
lift $ $(logInfo) "Installing GHC (this may take a while)"
|
||||||
lEM $ liftIO $ execLogged [s|./configure|]
|
lEM $ liftIO $ execLogged "./configure"
|
||||||
False
|
False
|
||||||
[[s|--prefix=|] <> toFilePath inst]
|
["--prefix=" <> toFilePath inst]
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
[rel|ghc-configure|]
|
||||||
(Just path)
|
|
||||||
Nothing
|
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
|
||||||
True
|
|
||||||
[[s|install|]]
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just path)
|
(Just path)
|
||||||
Nothing
|
Nothing
|
||||||
|
lEM $ liftIO $ make ["install"] (Just path)
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
|
|
||||||
@@ -169,34 +166,36 @@ 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
|
||||||
|
|
||||||
-- download (or use cached version)
|
-- download (or use cached version)
|
||||||
dlinfo <- liftE $ getDownloadInfo bDls Cabal ver mpfReq
|
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
|
||||||
|
|
||||||
-- prepare paths
|
-- prepare paths
|
||||||
bindir <- liftIO ghcupBinDir
|
bindir <- liftIO ghcupBinDir
|
||||||
|
|
||||||
-- the subdir of the archive where we do the work
|
-- the subdir of the archive where we do the work
|
||||||
let archiveSubdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
let workdir = maybe tmpUnpack (tmpUnpack </>) (view dlSubdir dlinfo)
|
||||||
|
|
||||||
liftE $ installCabal' archiveSubdir bindir
|
liftE $ installCabal' workdir bindir
|
||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
-- | 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
|
||||||
-> Excepts '[CopyError] m ()
|
-> Excepts '[CopyError] m ()
|
||||||
installCabal' path inst = do
|
installCabal' path inst = do
|
||||||
lift $ $(logInfo) [s|Installing cabal|]
|
lift $ $(logInfo) "Installing cabal"
|
||||||
let cabalFile = [rel|cabal|] :: Path Rel
|
let cabalFile = [rel|cabal|]
|
||||||
liftIO $ createDirIfMissing newDirPerms inst
|
liftIO $ createDirIfMissing newDirPerms inst
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile
|
||||||
(path </> cabalFile)
|
(path </> cabalFile)
|
||||||
@@ -221,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 ()
|
-> 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
|
||||||
@@ -235,20 +234,18 @@ 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
|
||||||
|
|
||||||
-- for ghc tools (ghc, ghci, haddock, ...)
|
-- for ghc tools (ghc, ghci, haddock, ...)
|
||||||
verfiles <- ghcToolFiles ver
|
verfiles <- ghcToolFiles ver
|
||||||
forM_ verfiles $ \file -> do
|
forM_ verfiles $ \file -> do
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile (bindir </> file)
|
|
||||||
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 <> [s|.|] <> 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)
|
||||||
|
|
||||||
@@ -259,9 +256,9 @@ 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 ()
|
pure ver
|
||||||
|
|
||||||
where
|
where
|
||||||
|
|
||||||
@@ -273,11 +270,11 @@ setGHC ver sghc = do
|
|||||||
destdir <- liftIO $ ghcupBaseDir
|
destdir <- liftIO $ ghcupBaseDir
|
||||||
case sghc of
|
case sghc of
|
||||||
SetGHCOnly -> do
|
SetGHCOnly -> do
|
||||||
let sharedir = [rel|share|] :: Path Rel
|
let sharedir = [rel|share|]
|
||||||
let fullsharedir = ghcdir </> sharedir
|
let fullsharedir = ghcdir </> sharedir
|
||||||
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
whenM (liftIO $ doesDirectoryExist fullsharedir) $ do
|
||||||
let fullF = destdir </> sharedir
|
let fullF = destdir </> sharedir
|
||||||
let targetF = [s|./ghc/|] <> verBS <> [s|/|] <> toFilePath sharedir
|
let targetF = "./ghc/" <> verBS <> "/" <> toFilePath sharedir
|
||||||
$(logDebug) [i|rm -f #{fullF}|]
|
$(logDebug) [i|rm -f #{fullF}|]
|
||||||
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
liftIO $ hideError doesNotExistErrorType $ deleteFile fullF
|
||||||
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
$(logDebug) [i|ln -s #{targetF} #{fullF}|]
|
||||||
@@ -299,27 +296,41 @@ 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
|
, lSet :: Bool -- ^ currently active version
|
||||||
, fromSrc :: Bool
|
, fromSrc :: Bool -- ^ compiled from source
|
||||||
|
, lStray :: Bool -- ^ not in download info
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
|
|
||||||
availableToolVersions :: GHCupDownloads -> Tool -> [(Version, [Tag])]
|
availableToolVersions :: GHCupDownloads -> Tool -> Map.Map Version [Tag]
|
||||||
availableToolVersions av tool = toListOf
|
availableToolVersions av tool = view
|
||||||
(ix tool % to (fmap (\(v, vi) -> (v, (_viTags vi))) . Map.toList) % folded)
|
(at tool % non Map.empty % to (fmap (_viTags)))
|
||||||
av
|
av
|
||||||
|
|
||||||
|
|
||||||
listVersions :: GHCupDownloads
|
-- | List all versions from the download info, as well as stray
|
||||||
|
-- versions.
|
||||||
|
listVersions :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
|
=> GHCupDownloads
|
||||||
-> Maybe Tool
|
-> Maybe Tool
|
||||||
-> Maybe ListCriteria
|
-> Maybe ListCriteria
|
||||||
-> IO [ListResult]
|
-> m [ListResult]
|
||||||
listVersions av lt criteria = case lt of
|
listVersions av lt criteria = case lt of
|
||||||
Just t -> do
|
Just t -> do
|
||||||
filter' <$> forM (availableToolVersions av t) (toListResult t)
|
-- get versions from GHCupDownloads
|
||||||
|
let avTools = availableToolVersions av t
|
||||||
|
lr <- filter' <$> forM (Map.toList avTools) (liftIO . toListResult t)
|
||||||
|
|
||||||
|
case t of
|
||||||
|
-- append stray GHCs
|
||||||
|
GHC -> do
|
||||||
|
slr <- strayGHCs avTools
|
||||||
|
pure $ (sort (slr ++ lr))
|
||||||
|
_ -> pure lr
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
ghcvers <- listVersions av (Just GHC) criteria
|
ghcvers <- listVersions av (Just GHC) criteria
|
||||||
cabalvers <- listVersions av (Just Cabal) criteria
|
cabalvers <- listVersions av (Just Cabal) criteria
|
||||||
@@ -327,21 +338,75 @@ listVersions av lt criteria = case lt of
|
|||||||
pure (ghcvers <> cabalvers <> ghcupvers)
|
pure (ghcvers <> cabalvers <> ghcupvers)
|
||||||
|
|
||||||
where
|
where
|
||||||
|
strayGHCs :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
|
=> Map.Map Version [Tag]
|
||||||
|
-> m [ListResult]
|
||||||
|
strayGHCs avTools = do
|
||||||
|
ghcs <- getInstalledGHCs
|
||||||
|
fmap catMaybes $ forM ghcs $ \case
|
||||||
|
Right tver@GHCTargetVersion{ _tvTarget = Nothing, .. } -> do
|
||||||
|
case Map.lookup _tvVersion avTools of
|
||||||
|
Just _ -> pure Nothing
|
||||||
|
Nothing -> do
|
||||||
|
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet Nothing
|
||||||
|
fromSrc <- liftIO $ ghcSrcInstalled tver
|
||||||
|
pure $ Just $ ListResult
|
||||||
|
{ lTool = GHC
|
||||||
|
, lVer = _tvVersion
|
||||||
|
, lCross = Nothing
|
||||||
|
, lTag = []
|
||||||
|
, lInstalled = True
|
||||||
|
, lStray = maybe True (const False) (Map.lookup _tvVersion avTools)
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
Right tver@GHCTargetVersion{ .. } -> do
|
||||||
|
lSet <- fmap (maybe False (\(GHCTargetVersion _ v ) -> v == _tvVersion)) $ ghcSet _tvTarget
|
||||||
|
fromSrc <- liftIO $ ghcSrcInstalled tver
|
||||||
|
pure $ Just $ ListResult
|
||||||
|
{ 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, .. }
|
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
|
||||||
lInstalled <- cabalInstalled v
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
pure ListResult { lVer = v
|
||||||
|
, lCross = Nothing
|
||||||
|
, lTag = tags
|
||||||
|
, lTool = t
|
||||||
|
, fromSrc = False
|
||||||
|
, lStray = False
|
||||||
|
, ..
|
||||||
|
}
|
||||||
GHCup -> do
|
GHCup -> do
|
||||||
let lSet = prettyPVP ghcUpVer == prettyVer v
|
let lSet = prettyPVP ghcUpVer == prettyVer v
|
||||||
let lInstalled = True
|
let lInstalled = lSet
|
||||||
pure ListResult { lVer = v, lTag = tags, lTool = t, fromSrc = False, .. }
|
pure ListResult { lVer = v
|
||||||
|
, lTag = tags
|
||||||
|
, lCross = Nothing
|
||||||
|
, lTool = t
|
||||||
|
, fromSrc = False
|
||||||
|
, lStray = False
|
||||||
|
, ..
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
filter' :: [ListResult] -> [ListResult]
|
filter' :: [ListResult] -> [ListResult]
|
||||||
@@ -359,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
|
||||||
@@ -371,6 +436,10 @@ rmGHCVer ver = do
|
|||||||
if exists
|
if exists
|
||||||
then do
|
then do
|
||||||
-- this isn't atomic, order matters
|
-- this isn't atomic, order matters
|
||||||
|
when isSetGHC $ do
|
||||||
|
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
||||||
|
liftE $ rmPlain (_tvTarget ver)
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Removing directory recursively: #{d'}|]
|
lift $ $(logInfo) [i|Removing directory recursively: #{d'}|]
|
||||||
liftIO $ deleteDirRecursive dir
|
liftIO $ deleteDirRecursive dir
|
||||||
|
|
||||||
@@ -381,20 +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)
|
||||||
|
|
||||||
|
|
||||||
when isSetGHC $ do
|
|
||||||
lift $ $(logInfo) [i|Removing ghc symlinks|]
|
|
||||||
liftE $ rmPlain ver
|
|
||||||
|
|
||||||
liftIO
|
liftIO
|
||||||
$ ghcupBaseDir
|
$ ghcupBaseDir
|
||||||
>>= hideError doesNotExistErrorType
|
>>= hideError doesNotExistErrorType
|
||||||
. deleteFile
|
. deleteFile
|
||||||
. (</> ([rel|share|] :: Path Rel))
|
. (</> [rel|share|])
|
||||||
else throwE (NotInstalled GHC ver)
|
else throwE (NotInstalled GHC (ver ^. tvVersion % to prettyVer))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -404,19 +468,18 @@ rmGHCVer ver = do
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadReader Settings m, MonadIO m)
|
getDebugInfo :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
=> Excepts
|
=> Excepts
|
||||||
'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
'[NoCompatiblePlatform , NoCompatibleArch , DistroNotFound]
|
||||||
m
|
m
|
||||||
DebugInfo
|
DebugInfo
|
||||||
getDebugInfo = do
|
getDebugInfo = do
|
||||||
diBaseDir <- liftIO $ ghcupBaseDir
|
diBaseDir <- liftIO $ ghcupBaseDir
|
||||||
diBinDir <- liftIO $ ghcupBinDir
|
diBinDir <- liftIO $ ghcupBinDir
|
||||||
diGHCDir <- liftIO $ ghcupGHCBaseDir
|
diGHCDir <- liftIO $ ghcupGHCBaseDir
|
||||||
diCacheDir <- liftIO $ ghcupCacheDir
|
diCacheDir <- liftIO $ ghcupCacheDir
|
||||||
diURLSource <- lift $ getUrlSource
|
diArch <- lE getArchitecture
|
||||||
diArch <- lE getArchitecture
|
diPlatform <- liftE $ getPlatform
|
||||||
diPlatform <- liftE $ getPlatform
|
|
||||||
pure $ DebugInfo { .. }
|
pure $ DebugInfo { .. }
|
||||||
|
|
||||||
|
|
||||||
@@ -436,92 +499,132 @@ compileGHC :: ( MonadMask m
|
|||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Version -- ^ version to install
|
-> GHCTargetVersion -- ^ version to install
|
||||||
-> Version -- ^ 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) -- ^ patch directory
|
||||||
|
-> [Text] -- ^ additional args to ./configure
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ AlreadyInstalled
|
'[ AlreadyInstalled
|
||||||
, BuildFailed
|
, BuildFailed
|
||||||
, DigestError
|
, DigestError
|
||||||
|
, DistroNotFound
|
||||||
, DownloadFailed
|
, DownloadFailed
|
||||||
, GHCupSetError
|
, GHCupSetError
|
||||||
|
, NoCompatibleArch
|
||||||
|
, NoCompatiblePlatform
|
||||||
, NoDownload
|
, NoDownload
|
||||||
|
, NotFoundInPATH
|
||||||
|
, PatchFailed
|
||||||
, UnknownArchive
|
, UnknownArchive
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
compileGHC dls tver bver jobs mbuildConfig = do
|
compileGHC dls tver bstrap jobs mbuildConfig patchdir aargs = do
|
||||||
lift $ $(logDebug) [i|Requested to compile: #{tver} with #{bver}|]
|
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
|
||||||
tmpUnpack <- lift mkGhcupTmpDir
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
(PlatformRequest {..}) <- liftE $ platformRequest
|
||||||
|
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
||||||
|
|
||||||
bghc <- parseRel ([s|ghc-|] <> verToBS bver)
|
bghc <- case bstrap of
|
||||||
|
Right g -> pure $ Right g
|
||||||
|
Left bver -> Left <$> parseRel ("ghc-" <> verToBS bver)
|
||||||
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
let workdir = maybe id (flip (</>)) (view dlSubdir dlInfo) $ tmpUnpack
|
||||||
ghcdir <- liftIO $ ghcupGHCDir tver
|
ghcdir <- liftIO $ ghcupGHCDir tver
|
||||||
|
|
||||||
catchAllE
|
liftE $ runBuildAction
|
||||||
(\es ->
|
tmpUnpack
|
||||||
liftIO (hideError doesNotExistErrorType $ deleteDirRecursive ghcdir)
|
(Just ghcdir)
|
||||||
>> throwE (BuildFailed workdir es)
|
(compile bghc ghcdir workdir >> markSrcBuilt ghcdir workdir)
|
||||||
)
|
|
||||||
$ 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)
|
||||||
=> Path Rel
|
=> Either (Path Rel) (Path Abs)
|
||||||
-> Path Abs
|
-> Path Abs
|
||||||
-> Path Abs
|
-> Path Abs
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[NoDownload , FileDoesNotExistError , ProcessError]
|
'[ FileDoesNotExistError
|
||||||
|
, InvalidBuildConfig
|
||||||
|
, PatchFailed
|
||||||
|
, ProcessError
|
||||||
|
, NotFoundInPATH
|
||||||
|
]
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
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
|
||||||
|
|
||||||
|
cEnv <- liftIO $ getEnvironment
|
||||||
|
|
||||||
if
|
if
|
||||||
| tver >= [vver|8.8.0|] -> do
|
| (_tvVersion tver) >= [vver|8.8.0|] -> do
|
||||||
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
bghcPath <- case bghc of
|
||||||
bghcPath <- (liftIO $ searchPath spaths bghc) !? NoDownload
|
Right ghc' -> pure ghc'
|
||||||
newEnv <- addToCurrentEnv [([s|GHC|], toFilePath bghcPath)]
|
Left bver -> do
|
||||||
lEM $ liftIO $ execLogged [s|./configure|]
|
spaths <- catMaybes . fmap parseAbs <$> liftIO getSearchPath
|
||||||
False
|
(liftIO $ searchPath spaths bver) !? NotFoundInPATH bver
|
||||||
[[s|--prefix=|] <> toFilePath ghcdir]
|
lEM $ liftIO $ execLogged
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
"./configure"
|
||||||
(Just workdir)
|
False
|
||||||
(Just newEnv)
|
( ["--prefix=" <> toFilePath ghcdir]
|
||||||
|
++ (maybe mempty
|
||||||
|
(\x -> ["--target=" <> E.encodeUtf8 x])
|
||||||
|
(_tvTarget tver)
|
||||||
|
)
|
||||||
|
++ fmap E.encodeUtf8 aargs
|
||||||
|
)
|
||||||
|
[rel|ghc-conf|]
|
||||||
|
(Just workdir)
|
||||||
|
(Just (("GHC", toFilePath bghcPath) : cEnv))
|
||||||
| otherwise -> do
|
| otherwise -> do
|
||||||
lEM $ liftIO $ execLogged
|
lEM $ liftIO $ execLogged
|
||||||
[s|./configure|]
|
"./configure"
|
||||||
False
|
False
|
||||||
[ [s|--prefix=|] <> toFilePath ghcdir
|
( [ "--prefix=" <> toFilePath ghcdir
|
||||||
, [s|--with-ghc=|] <> toFilePath bghc
|
, "--with-ghc=" <> either toFilePath toFilePath bghc
|
||||||
]
|
]
|
||||||
([rel|ghc-configure.log|] :: Path Rel)
|
++ (maybe mempty
|
||||||
|
(\x -> ["--target=" <> E.encodeUtf8 x])
|
||||||
|
(_tvTarget tver)
|
||||||
|
)
|
||||||
|
++ fmap E.encodeUtf8 aargs
|
||||||
|
)
|
||||||
|
[rel|ghc-conf|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
Nothing
|
(Just cEnv)
|
||||||
|
|
||||||
case mbuildConfig of
|
case mbuildConfig of
|
||||||
Just bc -> liftIOException
|
Just bc -> liftIOException
|
||||||
@@ -531,29 +634,42 @@ GhcWithLlvmCodeGen = YES|]
|
|||||||
Nothing ->
|
Nothing ->
|
||||||
liftIO $ writeFile (build_mk workdir) (Just newFilePerms) defaultConf
|
liftIO $ writeFile (build_mk workdir) (Just newFilePerms) defaultConf
|
||||||
|
|
||||||
lift
|
lift $ $(logInfo) [i|Building (this may take a while)...|]
|
||||||
$ $(logInfo)
|
lEM $ liftIO $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs)
|
||||||
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/ghc-make.log' to see the progress.|]
|
(Just workdir)
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
|
||||||
True
|
|
||||||
(maybe [] (\j -> [[s|-j|] <> fS (show j)]) jobs)
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
lift $ $(logInfo) [i|Installing...|]
|
lift $ $(logInfo) [i|Installing...|]
|
||||||
lEM $ liftIO $ execLogged [s|make|]
|
lEM $ liftIO $ make ["install"] (Just workdir)
|
||||||
True
|
|
||||||
[[s|install|]]
|
|
||||||
([rel|ghc-make.log|] :: Path Rel)
|
|
||||||
(Just workdir)
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
markSrcBuilt ghcdir workdir = do
|
markSrcBuilt ghcdir workdir = do
|
||||||
let dest = (ghcdir </> ghcUpSrcBuiltFile)
|
let dest = (ghcdir </> ghcUpSrcBuiltFile)
|
||||||
liftIO $ copyFile (build_mk workdir) dest Overwrite
|
liftIO $ copyFile (build_mk workdir) dest Overwrite
|
||||||
|
|
||||||
build_mk workdir = workdir </> ([rel|mk/build.mk|] :: Path Rel)
|
build_mk workdir = workdir </> [rel|mk/build.mk|]
|
||||||
|
|
||||||
|
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
|
||||||
@@ -564,19 +680,24 @@ compileCabal :: ( MonadReader Settings m
|
|||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Version -- ^ version to install
|
-> Version -- ^ version to install
|
||||||
-> Version -- ^ GHC version to build with
|
-> Either Version (Path Abs) -- ^ version to bootstrap with
|
||||||
-> Maybe Int
|
-> Maybe Int
|
||||||
|
-> Maybe (Path Abs)
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ BuildFailed
|
'[ BuildFailed
|
||||||
, DigestError
|
, DigestError
|
||||||
|
, DistroNotFound
|
||||||
, DownloadFailed
|
, DownloadFailed
|
||||||
|
, NoCompatibleArch
|
||||||
|
, NoCompatiblePlatform
|
||||||
, NoDownload
|
, NoDownload
|
||||||
|
, PatchFailed
|
||||||
, UnknownArchive
|
, UnknownArchive
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
()
|
()
|
||||||
compileCabal dls tver bver jobs = do
|
compileCabal dls tver bghc jobs patchdir = do
|
||||||
lift $ $(logDebug) [i|Requested to compile: #{tver} with ghc-#{bver}|]
|
lift $ $(logDebug) [i|Requested to compile: #{tver} with ghc-#{bghc}|]
|
||||||
|
|
||||||
-- download source tarball
|
-- download source tarball
|
||||||
dlInfo <- preview (ix Cabal % ix tver % viSourceDL % _Just) dls ?? NoDownload
|
dlInfo <- preview (ix Cabal % ix tver % viSourceDL % _Just) dls ?? NoDownload
|
||||||
@@ -585,10 +706,16 @@ compileCabal dls tver bver jobs = do
|
|||||||
-- unpack
|
-- unpack
|
||||||
tmpUnpack <- lift mkGhcupTmpDir
|
tmpUnpack <- lift mkGhcupTmpDir
|
||||||
liftE $ unpackToDir tmpUnpack dl
|
liftE $ unpackToDir tmpUnpack dl
|
||||||
|
(PlatformRequest {..}) <- liftE $ platformRequest
|
||||||
|
void $ liftIO $ darwinNotarization _rPlatform tmpUnpack
|
||||||
|
|
||||||
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
|
||||||
@@ -596,27 +723,38 @@ compileCabal dls tver bver jobs = do
|
|||||||
pure ()
|
pure ()
|
||||||
|
|
||||||
where
|
where
|
||||||
compile :: (MonadLogger m, MonadIO m)
|
compile :: (MonadThrow m, MonadLogger m, MonadIO m)
|
||||||
=> Path Abs
|
=> Path Abs
|
||||||
-> Excepts '[ProcessError] m ()
|
-> Excepts '[ProcessError , PatchFailed] m ()
|
||||||
compile workdir = do
|
compile workdir = do
|
||||||
lift
|
lift $ $(logInfo) [i|Building (this may take a while)...|]
|
||||||
$ $(logInfo)
|
|
||||||
[i|Building (this may take a while)... Run 'tail -f ~/.ghcup/logs/cabal-bootstrap.log' to see the progress.|]
|
forM_ patchdir $ \dir -> liftE $ applyPatches dir workdir
|
||||||
|
|
||||||
|
ghcEnv <- case bghc of
|
||||||
|
Right path -> do
|
||||||
|
-- recover the version from /foo/ghc-6.5.4
|
||||||
|
bn <- basename path
|
||||||
|
let dn = toFilePath $ dirname path
|
||||||
|
let ver = snd . B.break (== _hyphen) . toFilePath $ bn
|
||||||
|
|
||||||
|
pure
|
||||||
|
[ ("GHC" , toFilePath path)
|
||||||
|
, ("GHC_PKG", dn <> "/" <> "ghc-pkg" <> ver)
|
||||||
|
]
|
||||||
|
Left bver -> do
|
||||||
|
let v' = verToBS bver
|
||||||
|
pure [("GHC", "ghc-" <> v'), ("GHC_PKG", "ghc-pkg-" <> v')]
|
||||||
|
|
||||||
let v' = verToBS bver
|
|
||||||
cabal_bin <- liftIO $ ghcupBinDir
|
cabal_bin <- liftIO $ ghcupBinDir
|
||||||
newEnv <- lift $ addToCurrentEnv
|
newEnv <- lift
|
||||||
[ ([s|GHC|] , [s|ghc-|] <> v')
|
$ addToCurrentEnv (("PREFIX", toFilePath cabal_bin) : ghcEnv)
|
||||||
, ([s|GHC_PKG|], [s|ghc-pkg-|] <> v')
|
lift $ $(logDebug) [i|Environment: #{newEnv}|]
|
||||||
, ([s|GHC_VER|], v')
|
|
||||||
, ([s|PREFIX|] , toFilePath cabal_bin)
|
|
||||||
]
|
|
||||||
|
|
||||||
lEM $ liftIO $ execLogged [s|./bootstrap.sh|]
|
lEM $ liftIO $ execLogged "./bootstrap.sh"
|
||||||
False
|
False
|
||||||
(maybe [] (\j -> [[s|-j|], fS (show j)]) jobs)
|
(maybe [] (\j -> ["-j", fS (show j)]) jobs)
|
||||||
([rel|cabal-bootstrap.log|] :: Path Rel)
|
[rel|cabal-bootstrap|]
|
||||||
(Just workdir)
|
(Just workdir)
|
||||||
(Just newEnv)
|
(Just newEnv)
|
||||||
|
|
||||||
@@ -638,6 +776,8 @@ upgradeGHCup :: ( MonadMask m
|
|||||||
)
|
)
|
||||||
=> GHCupDownloads
|
=> GHCupDownloads
|
||||||
-> Maybe (Path Abs) -- ^ full file destination to write ghcup into
|
-> Maybe (Path Abs) -- ^ full file destination to write ghcup into
|
||||||
|
-> Bool -- ^ whether to force update regardless
|
||||||
|
-- of currently installed version
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ CopyError
|
'[ CopyError
|
||||||
, DigestError
|
, DigestError
|
||||||
@@ -646,23 +786,38 @@ upgradeGHCup :: ( MonadMask m
|
|||||||
, NoCompatibleArch
|
, NoCompatibleArch
|
||||||
, NoCompatiblePlatform
|
, NoCompatiblePlatform
|
||||||
, NoDownload
|
, NoDownload
|
||||||
|
, NoUpdate
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
Version
|
Version
|
||||||
upgradeGHCup dls mtarget = do
|
upgradeGHCup dls mtarget force = do
|
||||||
lift $ $(logInfo) [i|Upgrading GHCup...|]
|
lift $ $(logInfo) [i|Upgrading GHCup...|]
|
||||||
let latestVer = head $ getTagged dls GHCup Latest
|
let latestVer = fromJust $ getLatest dls GHCup
|
||||||
dli <- liftE $ getDownloadInfo dls GHCup latestVer Nothing
|
when (not force && (latestVer <= pvpToVersion ghcUpVer)) $ throwE NoUpdate
|
||||||
tmp <- lift withGHCupTmpDir
|
pfreq <- liftE platformRequest
|
||||||
let fn = [rel|ghcup|] :: Path Rel
|
dli <- lE $ getDownloadInfo GHCup latestVer pfreq dls
|
||||||
|
tmp <- lift withGHCupTmpDir
|
||||||
|
let fn = [rel|ghcup|]
|
||||||
p <- liftE $ download dli tmp (Just fn)
|
p <- liftE $ download dli tmp (Just fn)
|
||||||
|
let fileMode' =
|
||||||
|
newFilePerms
|
||||||
|
`unionFileModes` ownerExecuteMode
|
||||||
|
`unionFileModes` groupExecuteMode
|
||||||
|
`unionFileModes` otherExecuteMode
|
||||||
case mtarget of
|
case mtarget of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
dest <- liftIO $ ghcupBinDir
|
dest <- liftIO $ ghcupBinDir
|
||||||
|
liftIO $ hideError NoSuchThing $ deleteFile (dest </> fn)
|
||||||
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
||||||
(dest </> fn)
|
(dest </> fn)
|
||||||
Overwrite
|
Overwrite
|
||||||
Just fullDest -> liftIO $ copyFile p fullDest Overwrite
|
liftIO $ setFileMode (toFilePath (dest </> fn)) fileMode'
|
||||||
|
Just fullDest -> do
|
||||||
|
liftIO $ hideError NoSuchThing $ deleteFile fullDest
|
||||||
|
handleIO (throwE . CopyError . show) $ liftIO $ copyFile p
|
||||||
|
fullDest
|
||||||
|
Overwrite
|
||||||
|
liftIO $ setFileMode (toFilePath fullDest) fileMode'
|
||||||
pure latestVer
|
pure latestVer
|
||||||
|
|
||||||
|
|
||||||
@@ -675,12 +830,12 @@ upgradeGHCup dls mtarget = 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
|
||||||
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)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
11
lib/GHCup/Data/GHCupInfo.hs
Normal file
11
lib/GHCup/Data/GHCupInfo.hs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module GHCup.Data.GHCupInfo where
|
||||||
|
|
||||||
|
import GHCup.Data.GHCupDownloads
|
||||||
|
import GHCup.Data.ToolRequirements
|
||||||
|
import GHCup.Types
|
||||||
|
|
||||||
|
|
||||||
|
ghcupInfo :: GHCupInfo
|
||||||
|
ghcupInfo = GHCupInfo { _toolRequirements = toolRequirements
|
||||||
|
, _ghcupDownloads = ghcupDownloads
|
||||||
|
}
|
||||||
94
lib/GHCup/Data/ToolRequirements.hs
Normal file
94
lib/GHCup/Data/ToolRequirements.hs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
|
||||||
|
module GHCup.Data.ToolRequirements where
|
||||||
|
|
||||||
|
import GHCup.Types
|
||||||
|
import GHCup.Utils.String.QQ
|
||||||
|
|
||||||
|
import qualified Data.Map as M
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Currently 'GHC' is used for both GHC and cabal to simplify
|
||||||
|
-- this, until we need actual separation.
|
||||||
|
toolRequirements :: ToolRequirements
|
||||||
|
toolRequirements = M.fromList
|
||||||
|
[ ( GHC
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, M.fromList
|
||||||
|
[ ( Linux UnknownLinux
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[]
|
||||||
|
[s|You need the following packages: curl g++ gcc gmp make ncurses realpath xz-utils. Consult your distro documentation on the exact names of those packages.|]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, ( Linux Alpine
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[ "curl"
|
||||||
|
, "gcc"
|
||||||
|
, "g++"
|
||||||
|
, "gmp-dev"
|
||||||
|
, "ncurses-dev"
|
||||||
|
, "libffi-dev"
|
||||||
|
, "make"
|
||||||
|
, "xz"
|
||||||
|
, "tar"
|
||||||
|
, "perl"
|
||||||
|
]
|
||||||
|
""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, ( Linux Ubuntu
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[ "build-essential"
|
||||||
|
, "curl"
|
||||||
|
, "libgmp-dev"
|
||||||
|
, "libffi-dev"
|
||||||
|
, "libncurses-dev"
|
||||||
|
, "libtinfo5"
|
||||||
|
]
|
||||||
|
""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, ( Darwin
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[]
|
||||||
|
"On OS X, in the course of running ghcup you will be given a dialog box to install the command line tools. Accept and the requirements will be installed for you. You will then need to run the command again."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
, ( FreeBSD
|
||||||
|
, M.fromList
|
||||||
|
[ ( Nothing
|
||||||
|
, Requirements
|
||||||
|
[ "curl"
|
||||||
|
, "gcc"
|
||||||
|
, "gmp"
|
||||||
|
, "gmake"
|
||||||
|
, "ncurses"
|
||||||
|
, "perl5"
|
||||||
|
, "libffi"
|
||||||
|
, "libiconv"
|
||||||
|
]
|
||||||
|
""
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
@@ -1,84 +1,79 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCup.Download where
|
module GHCup.Download where
|
||||||
|
|
||||||
|
#if !defined(CURL)
|
||||||
|
import GHCup.Download.IOStreams
|
||||||
|
import GHCup.Download.Utils
|
||||||
|
#endif
|
||||||
import GHCup.Errors
|
import GHCup.Errors
|
||||||
import GHCup.Platform
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Types.JSON ( )
|
import GHCup.Types.JSON ( )
|
||||||
import GHCup.Types.Optics
|
import GHCup.Types.Optics
|
||||||
import GHCup.Utils
|
import GHCup.Utils
|
||||||
import GHCup.Utils.File
|
import GHCup.Utils.File
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
import GHCup.Version
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
import Control.Monad.Fail ( MonadFail )
|
import Control.Monad.Fail ( MonadFail )
|
||||||
|
#endif
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Class ( lift )
|
|
||||||
import Control.Monad.Trans.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 )
|
||||||
import Data.ByteString.Builder
|
|
||||||
import Data.CaseInsensitive ( CI )
|
import Data.CaseInsensitive ( CI )
|
||||||
import Data.IORef
|
#endif
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
import Data.Text.Read
|
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Time.Clock.POSIX
|
import Data.Time.Clock.POSIX
|
||||||
|
#if !defined(CURL)
|
||||||
import Data.Time.Format
|
import Data.Time.Format
|
||||||
|
#endif
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import GHC.IO.Exception
|
import GHC.IO.Exception
|
||||||
import HPath
|
import HPath
|
||||||
import HPath.IO
|
import HPath.IO as HIO
|
||||||
import Haskus.Utils.Variant.Excepts
|
import Haskus.Utils.Variant.Excepts
|
||||||
import Network.Http.Client hiding ( URL )
|
|
||||||
import OpenSSL.Digest
|
|
||||||
import Optics
|
import Optics
|
||||||
import Prelude hiding ( abs
|
import Prelude hiding ( abs
|
||||||
, readFile
|
, readFile
|
||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import "unix" System.Posix.IO.ByteString
|
|
||||||
hiding ( fdWrite )
|
|
||||||
import "unix-bytestring" System.Posix.IO.ByteString
|
|
||||||
( fdWrite )
|
|
||||||
import System.Posix.RawFilePath.Directory.Errors
|
|
||||||
( hideError )
|
|
||||||
import System.ProgressBar
|
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
import URI.ByteString.QQ
|
|
||||||
|
|
||||||
import qualified Data.Binary.Builder as B
|
import qualified Crypto.Hash.SHA256 as SHA256
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString.Base16 as B16
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
#if !defined(CURL)
|
||||||
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
|
||||||
|
#endif
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.IO.Streams as Streams
|
|
||||||
import qualified System.Posix.Files.ByteString as PF
|
import qualified System.Posix.Files.ByteString as PF
|
||||||
import qualified System.Posix.RawFilePath.Directory
|
import qualified System.Posix.RawFilePath.Directory
|
||||||
as RD
|
as RD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ghcupURL :: URI
|
|
||||||
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.1.json|]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -93,21 +88,20 @@ getDownloads :: ( FromJSONKey Tool
|
|||||||
, FromJSON VersionInfo
|
, FromJSON VersionInfo
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadCatch m
|
, MonadCatch m
|
||||||
, MonadReader Settings m
|
|
||||||
, MonadLogger m
|
, MonadLogger m
|
||||||
, MonadThrow m
|
, MonadThrow m
|
||||||
, MonadFail m
|
, MonadFail m
|
||||||
)
|
)
|
||||||
=> Excepts '[JSONError , DownloadFailed] m GHCupDownloads
|
=> URLSource
|
||||||
getDownloads = do
|
-> Excepts '[JSONError , DownloadFailed] m GHCupInfo
|
||||||
urlSource <- lift getUrlSource
|
getDownloads urlSource = do
|
||||||
lift $ $(logDebug) [i|Receiving download info from: #{urlSource}|]
|
lift $ $(logDebug) [i|Receiving download info from: #{urlSource}|]
|
||||||
case urlSource of
|
case urlSource of
|
||||||
GHCupURL -> do
|
GHCupURL -> do
|
||||||
bs <- reThrowAll DownloadFailed $ dl ghcupURL
|
bs <- reThrowAll DownloadFailed $ smartDl ghcupURL
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSource url) -> do
|
(OwnSource url) -> do
|
||||||
bs <- reThrowAll DownloadFailed $ dl url
|
bs <- reThrowAll DownloadFailed $ downloadBS url
|
||||||
lE' JSONDecodeError $ eitherDecode' bs
|
lE' JSONDecodeError $ eitherDecode' bs
|
||||||
(OwnSpec av) -> pure $ av
|
(OwnSpec av) -> pure $ av
|
||||||
|
|
||||||
@@ -121,24 +115,25 @@ getDownloads = do
|
|||||||
-- than the local file.
|
-- than the local file.
|
||||||
--
|
--
|
||||||
-- Always save the local file with the mod time of the remote file.
|
-- Always save the local file with the mod time of the remote file.
|
||||||
dl :: forall m1
|
smartDl :: forall m1
|
||||||
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
. (MonadCatch m1, MonadIO m1, MonadFail m1, MonadLogger m1)
|
||||||
=> URI
|
=> URI
|
||||||
-> Excepts
|
-> Excepts
|
||||||
'[ FileDoesNotExistError
|
'[ FileDoesNotExistError
|
||||||
, HTTPStatusError
|
, HTTPStatusError
|
||||||
, URIParseError
|
, URIParseError
|
||||||
, UnsupportedScheme
|
, UnsupportedScheme
|
||||||
, NoLocationHeader
|
, NoLocationHeader
|
||||||
, TooManyRedirs
|
, TooManyRedirs
|
||||||
]
|
, ProcessError
|
||||||
m1
|
]
|
||||||
L.ByteString
|
m1
|
||||||
dl uri' = do
|
L.ByteString
|
||||||
|
smartDl uri' = do
|
||||||
let path = view pathL' uri'
|
let path = view pathL' uri'
|
||||||
json_file <- (liftIO $ ghcupCacheDir)
|
cacheDir <- liftIO $ ghcupCacheDir
|
||||||
>>= \cacheDir -> (cacheDir </>) <$> urlBaseName path
|
json_file <- (cacheDir </>) <$> urlBaseName path
|
||||||
e <- liftIO $ doesFileExist json_file
|
e <- liftIO $ doesFileExist json_file
|
||||||
if e
|
if e
|
||||||
then do
|
then do
|
||||||
accessTime <-
|
accessTime <-
|
||||||
@@ -159,23 +154,27 @@ getDownloads = do
|
|||||||
pure bs
|
pure bs
|
||||||
else liftIO $ readFile json_file
|
else liftIO $ readFile json_file
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
lift $ $(logWarn) [i|Unable to get/parse Last-Modified header|]
|
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
||||||
liftIO $ deleteFile json_file
|
liftIO $ deleteFile json_file
|
||||||
liftE $ downloadBS uri'
|
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
|
||||||
getModTime >>= \case
|
getModTime >>= \case
|
||||||
Just modTime -> do
|
Just modTime -> do
|
||||||
bs <- liftE $ downloadBS uri'
|
bs <- liftE $ downloadBS uri'
|
||||||
liftIO $ writeFileWithModTime modTime json_file bs
|
liftIO $ writeFileWithModTime modTime json_file bs
|
||||||
pure bs
|
pure bs
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
lift $ $(logWarn) [i|Unable to get/parse Last-Modified header|]
|
lift $ $(logDebug) [i|Unable to get/parse Last-Modified header|]
|
||||||
liftE $ downloadBS uri'
|
liftE $ downloadBS uri'
|
||||||
|
|
||||||
where
|
where
|
||||||
getModTime = do
|
getModTime = do
|
||||||
|
#if defined(CURL)
|
||||||
|
pure Nothing
|
||||||
|
#else
|
||||||
headers <-
|
headers <-
|
||||||
handleIO (\_ -> pure mempty)
|
handleIO (\_ -> pure mempty)
|
||||||
$ liftE
|
$ liftE
|
||||||
@@ -187,14 +186,15 @@ getDownloads = do
|
|||||||
)
|
)
|
||||||
pure $ parseModifiedHeader headers
|
pure $ parseModifiedHeader headers
|
||||||
|
|
||||||
|
|
||||||
parseModifiedHeader :: (M.Map (CI ByteString) ByteString) -> Maybe UTCTime
|
parseModifiedHeader :: (M.Map (CI ByteString) ByteString) -> Maybe UTCTime
|
||||||
parseModifiedHeader headers =
|
parseModifiedHeader headers =
|
||||||
(M.lookup (CI.mk [s|Last-Modified|]) headers) >>= \h -> parseTimeM
|
(M.lookup (CI.mk "Last-Modified") headers) >>= \h -> parseTimeM
|
||||||
True
|
True
|
||||||
defaultTimeLocale
|
defaultTimeLocale
|
||||||
"%a, %d %b %Y %H:%M:%S %Z"
|
"%a, %d %b %Y %H:%M:%S %Z"
|
||||||
(T.unpack . E.decodeUtf8 $ h)
|
(T.unpack . decUTF8Safe $ h)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
writeFileWithModTime :: UTCTime -> Path Abs -> L.ByteString -> IO ()
|
writeFileWithModTime :: UTCTime -> Path Abs -> L.ByteString -> IO ()
|
||||||
writeFileWithModTime utctime path content = do
|
writeFileWithModTime utctime path content = do
|
||||||
@@ -203,47 +203,13 @@ getDownloads = do
|
|||||||
setModificationTimeHiRes path mod_time
|
setModificationTimeHiRes path mod_time
|
||||||
|
|
||||||
|
|
||||||
|
getDownloadInfo :: Tool
|
||||||
getDownloadInfo :: ( MonadLogger m
|
|
||||||
, MonadCatch m
|
|
||||||
, MonadIO m
|
|
||||||
, MonadReader Settings m
|
|
||||||
)
|
|
||||||
=> GHCupDownloads
|
|
||||||
-> Tool
|
|
||||||
-> Version
|
-> Version
|
||||||
-> Maybe PlatformRequest
|
|
||||||
-> Excepts
|
|
||||||
'[ DistroNotFound
|
|
||||||
, NoCompatiblePlatform
|
|
||||||
, NoCompatibleArch
|
|
||||||
, NoDownload
|
|
||||||
]
|
|
||||||
m
|
|
||||||
DownloadInfo
|
|
||||||
getDownloadInfo bDls t v mpfReq = do
|
|
||||||
(PlatformRequest arch' plat ver) <- case mpfReq of
|
|
||||||
Just x -> pure x
|
|
||||||
Nothing -> do
|
|
||||||
(PlatformResult rp rv) <- liftE getPlatform
|
|
||||||
ar <- lE getArchitecture
|
|
||||||
pure $ PlatformRequest ar rp rv
|
|
||||||
|
|
||||||
lE $ getDownloadInfo' t v arch' plat ver bDls
|
|
||||||
|
|
||||||
|
|
||||||
getDownloadInfo' :: Tool
|
|
||||||
-> Version
|
|
||||||
-- ^ tool version
|
-- ^ tool version
|
||||||
-> Architecture
|
-> PlatformRequest
|
||||||
-- ^ user arch
|
-> GHCupDownloads
|
||||||
-> Platform
|
-> Either NoDownload DownloadInfo
|
||||||
-- ^ user platform
|
getDownloadInfo t v (PlatformRequest a p mv) dls = maybe
|
||||||
-> Maybe Versioning
|
|
||||||
-- ^ optional version of the platform
|
|
||||||
-> GHCupDownloads
|
|
||||||
-> Either NoDownload DownloadInfo
|
|
||||||
getDownloadInfo' t v a p mv dls = maybe
|
|
||||||
(Left NoDownload)
|
(Left NoDownload)
|
||||||
Right
|
Right
|
||||||
(with_distro <|> without_distro_ver <|> without_distro)
|
(with_distro <|> without_distro_ver <|> without_distro)
|
||||||
@@ -275,9 +241,9 @@ download :: ( MonadMask m
|
|||||||
-> Maybe (Path Rel) -- ^ optional filename
|
-> Maybe (Path Rel) -- ^ optional filename
|
||||||
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
-> Excepts '[DigestError , DownloadFailed] m (Path Abs)
|
||||||
download dli dest mfn
|
download dli dest mfn
|
||||||
| scheme == [s|https|] = dl
|
| scheme == "https" = dl
|
||||||
| scheme == [s|http|] = dl
|
| scheme == "http" = dl
|
||||||
| scheme == [s|file|] = cp
|
| scheme == "file" = cp
|
||||||
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
| otherwise = throwE $ DownloadFailed (variantFromValue UnsupportedScheme)
|
||||||
|
|
||||||
where
|
where
|
||||||
@@ -290,22 +256,28 @@ download dli dest mfn
|
|||||||
liftIO $ copyFile fromFile destFile Strict
|
liftIO $ copyFile fromFile destFile Strict
|
||||||
pure destFile
|
pure destFile
|
||||||
dl = do
|
dl = do
|
||||||
let uri' = E.decodeUtf8 (serializeURIRef' (view dlUri dli))
|
let uri' = decUTF8Safe (serializeURIRef' (view dlUri dli))
|
||||||
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
lift $ $(logInfo) [i|downloading: #{uri'}|]
|
||||||
|
|
||||||
(https, host, fullPath, port) <- reThrowAll DownloadFailed
|
|
||||||
$ uriToQuadruple (view dlUri dli)
|
|
||||||
|
|
||||||
-- destination dir must exist
|
-- destination dir must exist
|
||||||
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
liftIO $ hideError AlreadyExists $ createDirRecursive newDirPerms dest
|
||||||
destFile <- getDestFile
|
destFile <- getDestFile
|
||||||
|
|
||||||
-- download
|
-- download
|
||||||
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
flip onException
|
||||||
let stepper = fdWrite fd
|
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
||||||
flip finally (liftIO $ closeFd fd)
|
$ catchAllE @_ @'[ProcessError, DownloadFailed, UnsupportedScheme]
|
||||||
$ reThrowAll DownloadFailed
|
(\e ->
|
||||||
$ downloadInternal True https host fullPath port stepper
|
(liftIO $ hideError doesNotExistErrorType $ deleteFile destFile)
|
||||||
|
>> (throwE . DownloadFailed $ e)
|
||||||
|
) $ do
|
||||||
|
#if defined(CURL)
|
||||||
|
liftE $ lEM @_ @'[ProcessError] $ liftIO $ exec "curl" True
|
||||||
|
["-fL", "-o", toFilePath destFile , serializeURIRef' $ view dlUri dli] Nothing Nothing
|
||||||
|
#else
|
||||||
|
(https, host, fullPath, port) <- liftE $ uriToQuadruple (view dlUri dli)
|
||||||
|
liftE $ downloadToFile https host fullPath port destFile
|
||||||
|
#endif
|
||||||
|
|
||||||
liftE $ checkDigest dli destFile
|
liftE $ checkDigest dli destFile
|
||||||
pure destFile
|
pure destFile
|
||||||
@@ -354,6 +326,8 @@ downloadCached dli mfn = do
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | This is used for downloading the JSON.
|
-- | This is used for downloading the JSON.
|
||||||
downloadBS :: (MonadCatch m, MonadIO m)
|
downloadBS :: (MonadCatch m, MonadIO m)
|
||||||
=> URI
|
=> URI
|
||||||
@@ -364,15 +338,16 @@ downloadBS :: (MonadCatch m, MonadIO m)
|
|||||||
, UnsupportedScheme
|
, UnsupportedScheme
|
||||||
, NoLocationHeader
|
, NoLocationHeader
|
||||||
, TooManyRedirs
|
, TooManyRedirs
|
||||||
|
, ProcessError
|
||||||
]
|
]
|
||||||
m
|
m
|
||||||
L.ByteString
|
L.ByteString
|
||||||
downloadBS uri'
|
downloadBS uri'
|
||||||
| scheme == [s|https|]
|
| scheme == "https"
|
||||||
= dl True
|
= dl True
|
||||||
| scheme == [s|http|]
|
| scheme == "http"
|
||||||
= dl False
|
= dl False
|
||||||
| scheme == [s|file|]
|
| scheme == "file"
|
||||||
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
= liftIOException doesNotExistErrorType (FileDoesNotExistError path)
|
||||||
$ (liftIO $ RD.readFile path)
|
$ (liftIO $ RD.readFile path)
|
||||||
| otherwise
|
| otherwise
|
||||||
@@ -381,235 +356,32 @@ 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)
|
||||||
|
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'
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
liftE $ downloadBS' https host' fullPath' port'
|
liftE $ downloadBS' https host' fullPath' port'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
-- | Load the result of this download into memory at once.
|
checkDigest :: (MonadIO m, MonadThrow m, MonadLogger m, MonadReader Settings m)
|
||||||
downloadBS' :: MonadIO m
|
|
||||||
=> Bool -- ^ https?
|
|
||||||
-> ByteString -- ^ host (e.g. "www.example.com")
|
|
||||||
-> ByteString -- ^ path (e.g. "/my/file") including query
|
|
||||||
-> Maybe Int -- ^ optional port (e.g. 3000)
|
|
||||||
-> Excepts
|
|
||||||
'[ HTTPStatusError
|
|
||||||
, URIParseError
|
|
||||||
, UnsupportedScheme
|
|
||||||
, NoLocationHeader
|
|
||||||
, TooManyRedirs
|
|
||||||
]
|
|
||||||
m
|
|
||||||
(L.ByteString)
|
|
||||||
downloadBS' https host path port = do
|
|
||||||
bref <- liftIO $ newIORef (mempty :: Builder)
|
|
||||||
let stepper bs = modifyIORef bref (<> byteString bs)
|
|
||||||
downloadInternal False https host path port stepper
|
|
||||||
liftIO (readIORef bref <&> toLazyByteString)
|
|
||||||
|
|
||||||
|
|
||||||
downloadInternal :: MonadIO m
|
|
||||||
=> Bool -- ^ whether to show a progress bar
|
|
||||||
-> Bool -- ^ https?
|
|
||||||
-> ByteString -- ^ host
|
|
||||||
-> ByteString -- ^ path with query
|
|
||||||
-> Maybe Int -- ^ optional port
|
|
||||||
-> (ByteString -> IO a) -- ^ the consuming step function
|
|
||||||
-> Excepts
|
|
||||||
'[ HTTPStatusError
|
|
||||||
, URIParseError
|
|
||||||
, UnsupportedScheme
|
|
||||||
, NoLocationHeader
|
|
||||||
, TooManyRedirs
|
|
||||||
]
|
|
||||||
m
|
|
||||||
()
|
|
||||||
downloadInternal = go (5 :: Int)
|
|
||||||
|
|
||||||
where
|
|
||||||
go redirs progressBar https host path port consumer = do
|
|
||||||
r <- liftIO $ withConnection' https host port action
|
|
||||||
veitherToExcepts r >>= \case
|
|
||||||
Just r' ->
|
|
||||||
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
|
||||||
Nothing -> pure ()
|
|
||||||
where
|
|
||||||
action c = do
|
|
||||||
let q = buildRequest1 $ http GET path
|
|
||||||
|
|
||||||
sendRequest c q emptyBody
|
|
||||||
|
|
||||||
receiveResponse
|
|
||||||
c
|
|
||||||
(\r i' -> runE $ do
|
|
||||||
let scode = getStatusCode r
|
|
||||||
if
|
|
||||||
| scode >= 200 && scode < 300 -> downloadStream r i' >> pure Nothing
|
|
||||||
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
|
||||||
Just r' -> pure $ Just $ r'
|
|
||||||
Nothing -> throwE NoLocationHeader
|
|
||||||
| otherwise -> throwE $ HTTPStatusError scode
|
|
||||||
)
|
|
||||||
|
|
||||||
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
|
||||||
Right uri' -> do
|
|
||||||
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
|
||||||
go (redirs - 1) progressBar https' host' fullPath' port' consumer
|
|
||||||
Left e -> throwE e
|
|
||||||
|
|
||||||
downloadStream r i' = do
|
|
||||||
let size = case getHeader r [s|Content-Length|] of
|
|
||||||
Just x' -> case decimal $ E.decodeUtf8 x' of
|
|
||||||
Left _ -> 0
|
|
||||||
Right (r', _) -> r'
|
|
||||||
Nothing -> 0
|
|
||||||
|
|
||||||
mpb <- if progressBar
|
|
||||||
then Just <$> (liftIO $ newProgressBar defStyle 10 (Progress 0 size ()))
|
|
||||||
else pure Nothing
|
|
||||||
|
|
||||||
outStream <- liftIO $ Streams.makeOutputStream
|
|
||||||
(\case
|
|
||||||
Just bs -> do
|
|
||||||
forM_ mpb $ \pb -> incProgress pb (BS.length bs)
|
|
||||||
void $ consumer bs
|
|
||||||
Nothing -> pure ()
|
|
||||||
)
|
|
||||||
liftIO $ Streams.connect i' outStream
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getHead :: (MonadCatch m, MonadIO m)
|
|
||||||
=> URI
|
|
||||||
-> Excepts
|
|
||||||
'[ HTTPStatusError
|
|
||||||
, URIParseError
|
|
||||||
, UnsupportedScheme
|
|
||||||
, NoLocationHeader
|
|
||||||
, TooManyRedirs
|
|
||||||
]
|
|
||||||
m
|
|
||||||
(M.Map (CI ByteString) ByteString)
|
|
||||||
getHead uri' | scheme == [s|https|] = head' True
|
|
||||||
| scheme == [s|http|] = head' False
|
|
||||||
| otherwise = throwE UnsupportedScheme
|
|
||||||
|
|
||||||
where
|
|
||||||
scheme = view (uriSchemeL' % schemeBSL') uri'
|
|
||||||
head' https = do
|
|
||||||
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
|
||||||
liftE $ headInternal https host' fullPath' port'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
headInternal :: MonadIO m
|
|
||||||
=> Bool -- ^ https?
|
|
||||||
-> ByteString -- ^ host
|
|
||||||
-> ByteString -- ^ path with query
|
|
||||||
-> Maybe Int -- ^ optional port
|
|
||||||
-> Excepts
|
|
||||||
'[ HTTPStatusError
|
|
||||||
, URIParseError
|
|
||||||
, UnsupportedScheme
|
|
||||||
, TooManyRedirs
|
|
||||||
, NoLocationHeader
|
|
||||||
]
|
|
||||||
m
|
|
||||||
(M.Map (CI ByteString) ByteString)
|
|
||||||
headInternal = go (5 :: Int)
|
|
||||||
|
|
||||||
where
|
|
||||||
go redirs https host path port = do
|
|
||||||
r <- liftIO $ withConnection' https host port action
|
|
||||||
veitherToExcepts r >>= \case
|
|
||||||
Left r' ->
|
|
||||||
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
|
||||||
Right hs -> pure hs
|
|
||||||
where
|
|
||||||
|
|
||||||
action c = do
|
|
||||||
let q = buildRequest1 $ http HEAD path
|
|
||||||
|
|
||||||
sendRequest c q emptyBody
|
|
||||||
|
|
||||||
unsafeReceiveResponse
|
|
||||||
c
|
|
||||||
(\r _ -> runE $ do
|
|
||||||
let scode = getStatusCode r
|
|
||||||
if
|
|
||||||
| scode >= 200 && scode < 300 -> do
|
|
||||||
let headers = getHeaderMap r
|
|
||||||
pure $ Right $ headers
|
|
||||||
| scode >= 300 && scode < 400 -> case getHeader r [s|Location|] of
|
|
||||||
Just r' -> pure $ Left $ r'
|
|
||||||
Nothing -> throwE NoLocationHeader
|
|
||||||
| otherwise -> throwE $ HTTPStatusError scode
|
|
||||||
)
|
|
||||||
|
|
||||||
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
|
||||||
Right uri' -> do
|
|
||||||
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
|
||||||
go (redirs - 1) https' host' fullPath' port'
|
|
||||||
Left e -> throwE e
|
|
||||||
|
|
||||||
|
|
||||||
withConnection' :: Bool
|
|
||||||
-> ByteString
|
|
||||||
-> Maybe Int
|
|
||||||
-> (Connection -> IO a)
|
|
||||||
-> IO a
|
|
||||||
withConnection' https host port action = bracket acquire closeConnection action
|
|
||||||
|
|
||||||
where
|
|
||||||
acquire = case https of
|
|
||||||
True -> do
|
|
||||||
ctx <- baselineContextSSL
|
|
||||||
openConnectionSSL ctx host (fromIntegral $ fromMaybe 443 port)
|
|
||||||
False -> openConnection host (fromIntegral $ fromMaybe 80 port)
|
|
||||||
|
|
||||||
|
|
||||||
-- | Extracts from a URI type: (https?, host, path+query, port)
|
|
||||||
uriToQuadruple :: Monad m
|
|
||||||
=> URI
|
|
||||||
-> Excepts
|
|
||||||
'[UnsupportedScheme]
|
|
||||||
m
|
|
||||||
(Bool, ByteString, ByteString, Maybe Int)
|
|
||||||
uriToQuadruple URI {..} = do
|
|
||||||
let scheme = view schemeBSL' uriScheme
|
|
||||||
|
|
||||||
host <-
|
|
||||||
preview (_Just % authorityHostL' % hostBSL') uriAuthority
|
|
||||||
?? UnsupportedScheme
|
|
||||||
|
|
||||||
https <- if
|
|
||||||
| scheme == [s|https|] -> pure True
|
|
||||||
| scheme == [s|http|] -> pure False
|
|
||||||
| otherwise -> throwE UnsupportedScheme
|
|
||||||
|
|
||||||
let
|
|
||||||
queryBS =
|
|
||||||
BS.intercalate [s|&|]
|
|
||||||
. fmap (\(x, y) -> encodeQuery x <> [s|=|] <> encodeQuery y)
|
|
||||||
$ (queryPairs uriQuery)
|
|
||||||
port =
|
|
||||||
preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority
|
|
||||||
fullpath =
|
|
||||||
if BS.null queryBS then uriPath else uriPath <> [s|?|] <> queryBS
|
|
||||||
pure (https, host, fullpath, port)
|
|
||||||
where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
|
|
||||||
|
|
||||||
|
|
||||||
checkDigest :: (MonadIO m, MonadLogger m, MonadReader Settings m)
|
|
||||||
=> DownloadInfo
|
=> DownloadInfo
|
||||||
-> Path Abs
|
-> Path Abs
|
||||||
-> Excepts '[DigestError] m ()
|
-> Excepts '[DigestError] m ()
|
||||||
checkDigest dli file = do
|
checkDigest dli file = do
|
||||||
verify <- lift ask <&> (not . noVerify)
|
verify <- lift ask <&> (not . noVerify)
|
||||||
when verify $ do
|
when verify $ do
|
||||||
let p' = toFilePath file
|
p' <- toFilePath <$> basename file
|
||||||
lift $ $(logInfo) [i|veryfing digest of: #{p'}|]
|
lift $ $(logInfo) [i|verifying digest of: #{p'}|]
|
||||||
c <- liftIO $ readFile file
|
c <- liftIO $ readFile file
|
||||||
let cDigest = E.decodeUtf8 . toHex . digest (digestByName "sha256") $ c
|
cDigest <- throwEither . E.decodeUtf8' . B16.encode . SHA256.hashlazy $ c
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
253
lib/GHCup/Download/IOStreams.hs
Normal file
253
lib/GHCup/Download/IOStreams.hs
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
|
||||||
|
module GHCup.Download.IOStreams where
|
||||||
|
|
||||||
|
|
||||||
|
import GHCup.Download.Utils
|
||||||
|
import GHCup.Errors
|
||||||
|
import GHCup.Types.Optics
|
||||||
|
import GHCup.Types.JSON ( )
|
||||||
|
import GHCup.Utils.File
|
||||||
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Exception.Safe
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Data.ByteString ( ByteString )
|
||||||
|
import Data.ByteString.Builder
|
||||||
|
import Data.CaseInsensitive ( CI )
|
||||||
|
import Data.IORef
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Text.Read
|
||||||
|
import HPath
|
||||||
|
import HPath.IO as HIO
|
||||||
|
import Haskus.Utils.Variant.Excepts
|
||||||
|
import Network.Http.Client hiding ( URL )
|
||||||
|
import Optics
|
||||||
|
import Prelude hiding ( abs
|
||||||
|
, readFile
|
||||||
|
, writeFile
|
||||||
|
)
|
||||||
|
import "unix" System.Posix.IO.ByteString
|
||||||
|
hiding ( fdWrite )
|
||||||
|
import "unix-bytestring" System.Posix.IO.ByteString
|
||||||
|
( fdWrite )
|
||||||
|
import System.ProgressBar
|
||||||
|
import URI.ByteString
|
||||||
|
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
import qualified Data.Map.Strict as M
|
||||||
|
import qualified System.IO.Streams as Streams
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
--[ Low-level (non-curl) ]--
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Load the result of this download into memory at once.
|
||||||
|
downloadBS' :: MonadIO m
|
||||||
|
=> Bool -- ^ https?
|
||||||
|
-> ByteString -- ^ host (e.g. "www.example.com")
|
||||||
|
-> ByteString -- ^ path (e.g. "/my/file") including query
|
||||||
|
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(L.ByteString)
|
||||||
|
downloadBS' https host path port = do
|
||||||
|
bref <- liftIO $ newIORef (mempty :: Builder)
|
||||||
|
let stepper bs = modifyIORef bref (<> byteString bs)
|
||||||
|
downloadInternal False https host path port stepper
|
||||||
|
liftIO (readIORef bref <&> toLazyByteString)
|
||||||
|
|
||||||
|
|
||||||
|
downloadToFile :: (MonadMask m, MonadIO m)
|
||||||
|
=> Bool -- ^ https?
|
||||||
|
-> ByteString -- ^ host (e.g. "www.example.com")
|
||||||
|
-> ByteString -- ^ path (e.g. "/my/file") including query
|
||||||
|
-> Maybe Int -- ^ optional port (e.g. 3000)
|
||||||
|
-> Path Abs -- ^ destination file to create and write to
|
||||||
|
-> Excepts '[DownloadFailed] m ()
|
||||||
|
downloadToFile https host fullPath port destFile = do
|
||||||
|
fd <- liftIO $ createRegularFileFd newFilePerms destFile
|
||||||
|
let stepper = fdWrite fd
|
||||||
|
flip finally (liftIO $ closeFd fd)
|
||||||
|
$ reThrowAll DownloadFailed $ downloadInternal True https host fullPath port stepper
|
||||||
|
|
||||||
|
|
||||||
|
downloadInternal :: MonadIO m
|
||||||
|
=> Bool -- ^ whether to show a progress bar
|
||||||
|
-> Bool -- ^ https?
|
||||||
|
-> ByteString -- ^ host
|
||||||
|
-> ByteString -- ^ path with query
|
||||||
|
-> Maybe Int -- ^ optional port
|
||||||
|
-> (ByteString -> IO a) -- ^ the consuming step function
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
]
|
||||||
|
m
|
||||||
|
()
|
||||||
|
downloadInternal = go (5 :: Int)
|
||||||
|
|
||||||
|
where
|
||||||
|
go redirs progressBar https host path port consumer = do
|
||||||
|
r <- liftIO $ withConnection' https host port action
|
||||||
|
veitherToExcepts r >>= \case
|
||||||
|
Just r' ->
|
||||||
|
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
||||||
|
Nothing -> pure ()
|
||||||
|
where
|
||||||
|
action c = do
|
||||||
|
let q = buildRequest1 $ http GET path
|
||||||
|
|
||||||
|
sendRequest c q emptyBody
|
||||||
|
|
||||||
|
receiveResponse
|
||||||
|
c
|
||||||
|
(\r i' -> runE $ do
|
||||||
|
let scode = getStatusCode r
|
||||||
|
if
|
||||||
|
| scode >= 200 && scode < 300 -> downloadStream r i' >> pure Nothing
|
||||||
|
| scode >= 300 && scode < 400 -> case getHeader r "Location" of
|
||||||
|
Just r' -> pure $ Just $ r'
|
||||||
|
Nothing -> throwE NoLocationHeader
|
||||||
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
|
)
|
||||||
|
|
||||||
|
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
||||||
|
Right uri' -> do
|
||||||
|
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
go (redirs - 1) progressBar https' host' fullPath' port' consumer
|
||||||
|
Left e -> throwE e
|
||||||
|
|
||||||
|
downloadStream r i' = do
|
||||||
|
let size = case getHeader r "Content-Length" of
|
||||||
|
Just x' -> case decimal $ decUTF8Safe x' of
|
||||||
|
Left _ -> 0
|
||||||
|
Right (r', _) -> r'
|
||||||
|
Nothing -> 0
|
||||||
|
|
||||||
|
mpb <- if progressBar
|
||||||
|
then Just <$> (liftIO $ newProgressBar defStyle 10 (Progress 0 size ()))
|
||||||
|
else pure Nothing
|
||||||
|
|
||||||
|
outStream <- liftIO $ Streams.makeOutputStream
|
||||||
|
(\case
|
||||||
|
Just bs -> do
|
||||||
|
forM_ mpb $ \pb -> incProgress pb (BS.length bs)
|
||||||
|
void $ consumer bs
|
||||||
|
Nothing -> pure ()
|
||||||
|
)
|
||||||
|
liftIO $ Streams.connect i' outStream
|
||||||
|
|
||||||
|
|
||||||
|
getHead :: (MonadCatch m, MonadIO m)
|
||||||
|
=> URI
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, NoLocationHeader
|
||||||
|
, TooManyRedirs
|
||||||
|
, ProcessError
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(M.Map (CI ByteString) ByteString)
|
||||||
|
getHead uri' | scheme == "https" = head' True
|
||||||
|
| scheme == "http" = head' False
|
||||||
|
| otherwise = throwE UnsupportedScheme
|
||||||
|
|
||||||
|
where
|
||||||
|
scheme = view (uriSchemeL' % schemeBSL') uri'
|
||||||
|
head' https = do
|
||||||
|
(_, host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
liftE $ headInternal https host' fullPath' port'
|
||||||
|
|
||||||
|
|
||||||
|
headInternal :: MonadIO m
|
||||||
|
=> Bool -- ^ https?
|
||||||
|
-> ByteString -- ^ host
|
||||||
|
-> ByteString -- ^ path with query
|
||||||
|
-> Maybe Int -- ^ optional port
|
||||||
|
-> Excepts
|
||||||
|
'[ HTTPStatusError
|
||||||
|
, URIParseError
|
||||||
|
, UnsupportedScheme
|
||||||
|
, TooManyRedirs
|
||||||
|
, NoLocationHeader
|
||||||
|
]
|
||||||
|
m
|
||||||
|
(M.Map (CI ByteString) ByteString)
|
||||||
|
headInternal = go (5 :: Int)
|
||||||
|
|
||||||
|
where
|
||||||
|
go redirs https host path port = do
|
||||||
|
r <- liftIO $ withConnection' https host port action
|
||||||
|
veitherToExcepts r >>= \case
|
||||||
|
Left r' ->
|
||||||
|
if redirs > 0 then followRedirectURL r' else throwE TooManyRedirs
|
||||||
|
Right hs -> pure hs
|
||||||
|
where
|
||||||
|
|
||||||
|
action c = do
|
||||||
|
let q = buildRequest1 $ http HEAD path
|
||||||
|
|
||||||
|
sendRequest c q emptyBody
|
||||||
|
|
||||||
|
unsafeReceiveResponse
|
||||||
|
c
|
||||||
|
(\r _ -> runE $ do
|
||||||
|
let scode = getStatusCode r
|
||||||
|
if
|
||||||
|
| scode >= 200 && scode < 300 -> do
|
||||||
|
let headers = getHeaderMap r
|
||||||
|
pure $ Right $ headers
|
||||||
|
| scode >= 300 && scode < 400 -> case getHeader r "Location" of
|
||||||
|
Just r' -> pure $ Left $ r'
|
||||||
|
Nothing -> throwE NoLocationHeader
|
||||||
|
| otherwise -> throwE $ HTTPStatusError scode
|
||||||
|
)
|
||||||
|
|
||||||
|
followRedirectURL bs = case parseURI strictURIParserOptions bs of
|
||||||
|
Right uri' -> do
|
||||||
|
(https', host', fullPath', port') <- liftE $ uriToQuadruple uri'
|
||||||
|
go (redirs - 1) https' host' fullPath' port'
|
||||||
|
Left e -> throwE e
|
||||||
|
|
||||||
|
|
||||||
|
withConnection' :: Bool
|
||||||
|
-> ByteString
|
||||||
|
-> Maybe Int
|
||||||
|
-> (Connection -> IO a)
|
||||||
|
-> IO a
|
||||||
|
withConnection' https host port action = bracket acquire closeConnection action
|
||||||
|
|
||||||
|
where
|
||||||
|
acquire = case https of
|
||||||
|
True -> do
|
||||||
|
ctx <- baselineContextSSL
|
||||||
|
openConnectionSSL ctx host (fromIntegral $ fromMaybe 443 port)
|
||||||
|
False -> openConnection host (fromIntegral $ fromMaybe 80 port)
|
||||||
64
lib/GHCup/Download/Utils.hs
Normal file
64
lib/GHCup/Download/Utils.hs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
|
||||||
|
module GHCup.Download.Utils where
|
||||||
|
|
||||||
|
|
||||||
|
import GHCup.Errors
|
||||||
|
import GHCup.Types.Optics
|
||||||
|
import GHCup.Types.JSON ( )
|
||||||
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Data.ByteString ( ByteString )
|
||||||
|
import Data.Maybe
|
||||||
|
import Haskus.Utils.Variant.Excepts
|
||||||
|
import Optics
|
||||||
|
import Prelude hiding ( abs
|
||||||
|
, readFile
|
||||||
|
, writeFile
|
||||||
|
)
|
||||||
|
import URI.ByteString
|
||||||
|
|
||||||
|
import qualified Data.Binary.Builder as B
|
||||||
|
import qualified Data.ByteString as BS
|
||||||
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
|
||||||
|
|
||||||
|
-- | Extracts from a URI type: (https?, host, path+query, port)
|
||||||
|
uriToQuadruple :: Monad m
|
||||||
|
=> URI
|
||||||
|
-> Excepts
|
||||||
|
'[UnsupportedScheme]
|
||||||
|
m
|
||||||
|
(Bool, ByteString, ByteString, Maybe Int)
|
||||||
|
uriToQuadruple URI {..} = do
|
||||||
|
let scheme = view schemeBSL' uriScheme
|
||||||
|
|
||||||
|
host <-
|
||||||
|
preview (_Just % authorityHostL' % hostBSL') uriAuthority
|
||||||
|
?? UnsupportedScheme
|
||||||
|
|
||||||
|
https <- if
|
||||||
|
| scheme == "https" -> pure True
|
||||||
|
| scheme == "http" -> pure False
|
||||||
|
| otherwise -> throwE UnsupportedScheme
|
||||||
|
|
||||||
|
let queryBS =
|
||||||
|
BS.intercalate "&"
|
||||||
|
. fmap (\(x, y) -> encodeQuery x <> "=" <> encodeQuery y)
|
||||||
|
$ (queryPairs uriQuery)
|
||||||
|
port =
|
||||||
|
preview (_Just % authorityPortL' % _Just % portNumberL') uriAuthority
|
||||||
|
fullpath = if BS.null queryBS then uriPath else uriPath <> "?" <> queryBS
|
||||||
|
pure (https, host, fullpath, port)
|
||||||
|
where encodeQuery = L.toStrict . B.toLazyByteString . urlEncodeQuery
|
||||||
|
|
||||||
@@ -30,6 +30,10 @@ data NoCompatiblePlatform = NoCompatiblePlatform String -- the platform we got
|
|||||||
data NoDownload = NoDownload
|
data NoDownload = NoDownload
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | No update available or necessary.
|
||||||
|
data NoUpdate = NoUpdate
|
||||||
|
deriving Show
|
||||||
|
|
||||||
-- | The Architecture is unknown and unsupported.
|
-- | The Architecture is unknown and unsupported.
|
||||||
data NoCompatibleArch = NoCompatibleArch String
|
data NoCompatibleArch = NoCompatibleArch String
|
||||||
deriving Show
|
deriving Show
|
||||||
@@ -60,7 +64,11 @@ 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
|
||||||
|
|
||||||
|
-- | An executable was expected to be in PATH, but was not found.
|
||||||
|
data NotFoundInPATH = NotFoundInPATH (Path Rel)
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
-- | JSON decoding failed.
|
-- | JSON decoding failed.
|
||||||
@@ -88,6 +96,16 @@ data NoLocationHeader = NoLocationHeader
|
|||||||
data TooManyRedirs = TooManyRedirs
|
data TooManyRedirs = TooManyRedirs
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
-- | A patch could not be applied.
|
||||||
|
data PatchFailed = PatchFailed
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
-- | The tool requirements could not be found.
|
||||||
|
data NoToolRequirements = NoToolRequirements
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
data InvalidBuildConfig = InvalidBuildConfig Text
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
@@ -20,7 +22,7 @@ import Control.Exception.Safe
|
|||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Class ( lift )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String.Interpolate
|
import Data.String.Interpolate
|
||||||
@@ -34,16 +36,30 @@ import Prelude hiding ( abs
|
|||||||
, writeFile
|
, writeFile
|
||||||
)
|
)
|
||||||
import System.Info
|
import System.Info
|
||||||
|
import Text.Regex.Posix
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
|
||||||
import qualified Data.Text.ICU as ICU
|
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
--[ Platform detection ]--
|
--[ Platform detection ]--
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get the full platform request, consisting of architecture, distro, ...
|
||||||
|
platformRequest :: (MonadLogger m, MonadCatch m, MonadIO m)
|
||||||
|
=> Excepts
|
||||||
|
'[ NoCompatiblePlatform
|
||||||
|
, NoCompatibleArch
|
||||||
|
, DistroNotFound
|
||||||
|
]
|
||||||
|
m
|
||||||
|
PlatformRequest
|
||||||
|
platformRequest = do
|
||||||
|
(PlatformResult rp rv) <- liftE getPlatform
|
||||||
|
ar <- lE getArchitecture
|
||||||
|
pure $ PlatformRequest ar rp rv
|
||||||
|
|
||||||
|
|
||||||
getArchitecture :: Either NoCompatibleArch Architecture
|
getArchitecture :: Either NoCompatibleArch Architecture
|
||||||
getArchitecture = case arch of
|
getArchitecture = case arch of
|
||||||
"x86_64" -> Right A_64
|
"x86_64" -> Right A_64
|
||||||
@@ -62,16 +78,30 @@ getPlatform = do
|
|||||||
"linux" -> do
|
"linux" -> do
|
||||||
(distro, ver) <- liftE getLinuxDistro
|
(distro, ver) <- liftE getLinuxDistro
|
||||||
pure $ PlatformResult { _platform = Linux distro, _distroVersion = ver }
|
pure $ PlatformResult { _platform = Linux distro, _distroVersion = ver }
|
||||||
-- TODO: these are not verified
|
"darwin" -> do
|
||||||
"darwin" ->
|
ver <-
|
||||||
pure $ PlatformResult { _platform = Darwin, _distroVersion = Nothing }
|
( either (const Nothing) Just
|
||||||
|
. versioning
|
||||||
|
. getMajorVersion
|
||||||
|
. decUTF8Safe
|
||||||
|
)
|
||||||
|
<$> getDarwinVersion
|
||||||
|
pure $ PlatformResult { _platform = Darwin, _distroVersion = ver }
|
||||||
"freebsd" -> do
|
"freebsd" -> do
|
||||||
ver <- getFreeBSDVersion
|
ver <-
|
||||||
|
(either (const Nothing) Just . versioning . decUTF8Safe)
|
||||||
|
<$> getFreeBSDVersion
|
||||||
pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }
|
pure $ PlatformResult { _platform = FreeBSD, _distroVersion = ver }
|
||||||
what -> throwE $ NoCompatiblePlatform what
|
what -> throwE $ NoCompatiblePlatform what
|
||||||
lift $ $(logDebug) [i|Identified Platform as: #{pfr}|]
|
lift $ $(logDebug) [i|Identified Platform as: #{pfr}|]
|
||||||
pure pfr
|
pure pfr
|
||||||
where getFreeBSDVersion = pure Nothing
|
where
|
||||||
|
getMajorVersion = T.intercalate "." . take 2 . T.split (== '.')
|
||||||
|
getFreeBSDVersion =
|
||||||
|
liftIO $ fmap _stdOut $ executeOut [rel|freebsd-version|] [] Nothing
|
||||||
|
getDarwinVersion = liftIO $ fmap _stdOut $ executeOut [rel|sw_vers|]
|
||||||
|
["-productVersion"]
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
getLinuxDistro :: (MonadCatch m, MonadIO m)
|
getLinuxDistro :: (MonadCatch m, MonadIO m)
|
||||||
@@ -100,16 +130,11 @@ getLinuxDistro = do
|
|||||||
| otherwise -> UnknownLinux
|
| otherwise -> UnknownLinux
|
||||||
pure (distro, parsedVer)
|
pure (distro, parsedVer)
|
||||||
where
|
where
|
||||||
hasWord t matches = foldr
|
hasWord t matches = foldr (\x y -> match (regex x) (T.unpack t) || y)
|
||||||
(\x y ->
|
False
|
||||||
( isJust
|
matches
|
||||||
. ICU.find (ICU.regex [ICU.CaseInsensitive] ([s|\b|] <> x <> [s|\b|]))
|
where
|
||||||
$ t
|
regex x = makeRegexOpts compIgnoreCase execBlank ([s|\<|] ++ x ++ [s|\>|])
|
||||||
)
|
|
||||||
|| y
|
|
||||||
)
|
|
||||||
False
|
|
||||||
(T.pack <$> matches)
|
|
||||||
|
|
||||||
os_release :: Path Abs
|
os_release :: Path Abs
|
||||||
os_release = [abs|/etc/os-release|]
|
os_release = [abs|/etc/os-release|]
|
||||||
@@ -131,9 +156,9 @@ getLinuxDistro = do
|
|||||||
try_lsb_release_cmd :: IO (Text, Maybe Text)
|
try_lsb_release_cmd :: IO (Text, Maybe Text)
|
||||||
try_lsb_release_cmd = do
|
try_lsb_release_cmd = do
|
||||||
(Just _) <- findExecutable lsb_release_cmd
|
(Just _) <- findExecutable lsb_release_cmd
|
||||||
name <- fmap _stdOut $ executeOut lsb_release_cmd [[s|-si|]] Nothing
|
name <- fmap _stdOut $ executeOut lsb_release_cmd ["-si"] Nothing
|
||||||
ver <- fmap _stdOut $ executeOut lsb_release_cmd [[s|-sr|]] Nothing
|
ver <- fmap _stdOut $ executeOut lsb_release_cmd ["-sr"] Nothing
|
||||||
pure (E.decodeUtf8 name, Just $ E.decodeUtf8 ver)
|
pure (decUTF8Safe name, Just $ decUTF8Safe ver)
|
||||||
|
|
||||||
try_lsb_release :: IO (Text, Maybe Text)
|
try_lsb_release :: IO (Text, Maybe Text)
|
||||||
try_lsb_release = do
|
try_lsb_release = do
|
||||||
@@ -143,24 +168,27 @@ getLinuxDistro = do
|
|||||||
|
|
||||||
try_redhat_release :: IO (Text, Maybe Text)
|
try_redhat_release :: IO (Text, Maybe Text)
|
||||||
try_redhat_release = do
|
try_redhat_release = do
|
||||||
t <- fmap lBS2sT $ readFile redhat_release
|
t <- fmap decUTF8Safe' $ readFile redhat_release
|
||||||
|
let nameRegex n =
|
||||||
|
makeRegexOpts compIgnoreCase
|
||||||
|
execBlank
|
||||||
|
(([s|\<|] <> fS n <> [s|\>|] :: ByteString)) :: Regex
|
||||||
|
let verRegex =
|
||||||
|
makeRegexOpts compIgnoreCase
|
||||||
|
execBlank
|
||||||
|
([s|\<([0-9])+(.([0-9])+)*\>|] :: ByteString) :: Regex
|
||||||
let nameRe n =
|
let nameRe n =
|
||||||
join
|
fromEmpty . match (nameRegex n) $ T.unpack t :: Maybe String
|
||||||
. fmap (ICU.group 0)
|
verRe = fromEmpty . match verRegex $ T.unpack t :: Maybe String
|
||||||
. ICU.find
|
|
||||||
(ICU.regex [ICU.CaseInsensitive] ([s|\b|] <> fS n <> [s|\b|]))
|
|
||||||
$ t
|
|
||||||
verRe =
|
|
||||||
join
|
|
||||||
. fmap (ICU.group 0)
|
|
||||||
. ICU.find
|
|
||||||
(ICU.regex [ICU.CaseInsensitive] [s|\b(\d)+(.(\d)+)*\b|])
|
|
||||||
$ t
|
|
||||||
(Just name) <- pure
|
(Just name) <- pure
|
||||||
(nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")
|
(nameRe "CentOS" <|> nameRe "Fedora" <|> nameRe "Red Hat")
|
||||||
pure (name, verRe)
|
pure (T.pack name, fmap T.pack verRe)
|
||||||
|
where
|
||||||
|
fromEmpty :: String -> Maybe String
|
||||||
|
fromEmpty "" = Nothing
|
||||||
|
fromEmpty s' = Just s'
|
||||||
|
|
||||||
try_debian_version :: IO (Text, Maybe Text)
|
try_debian_version :: IO (Text, Maybe Text)
|
||||||
try_debian_version = do
|
try_debian_version = do
|
||||||
ver <- readFile debian_version
|
ver <- readFile debian_version
|
||||||
pure (T.pack "debian", Just $ lBS2sT ver)
|
pure (T.pack "debian", Just . decUTF8Safe' $ ver)
|
||||||
|
|||||||
46
lib/GHCup/Requirements.hs
Normal file
46
lib/GHCup/Requirements.hs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module GHCup.Requirements where
|
||||||
|
|
||||||
|
import GHCup.Types
|
||||||
|
import GHCup.Types.JSON ( )
|
||||||
|
import GHCup.Types.Optics
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Data.Maybe
|
||||||
|
import Optics
|
||||||
|
import Prelude hiding ( abs
|
||||||
|
, readFile
|
||||||
|
, writeFile
|
||||||
|
)
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
|
||||||
|
-- | Get the requirements. Right now this combines GHC and cabal
|
||||||
|
-- and doesn't do fine-grained distinction. However, the 'ToolRequirements'
|
||||||
|
-- type allows it.
|
||||||
|
getCommonRequirements :: PlatformResult
|
||||||
|
-> ToolRequirements
|
||||||
|
-> Maybe Requirements
|
||||||
|
getCommonRequirements pr tr =
|
||||||
|
preview (ix GHC % ix Nothing % ix (_platform pr) % ix (_distroVersion pr)) tr
|
||||||
|
<|> preview (ix GHC % ix Nothing % ix (_platform pr) % ix Nothing) tr
|
||||||
|
<|> preview
|
||||||
|
( ix GHC
|
||||||
|
% ix Nothing
|
||||||
|
% ix (set _Linux UnknownLinux $ _platform pr)
|
||||||
|
% ix Nothing
|
||||||
|
)
|
||||||
|
tr
|
||||||
|
|
||||||
|
|
||||||
|
prettyRequirements :: Requirements -> T.Text
|
||||||
|
prettyRequirements Requirements {..} =
|
||||||
|
let d = if not . null $ _distroPKGs
|
||||||
|
then
|
||||||
|
"\n Install the following distro packages: "
|
||||||
|
<> T.intercalate " " _distroPKGs
|
||||||
|
else ""
|
||||||
|
n = if not . T.null $ _notes then "\n Note: " <> _notes else ""
|
||||||
|
in "System requirements " <> d <> n
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module GHCup.Types where
|
module GHCup.Types where
|
||||||
|
|
||||||
@@ -12,6 +13,39 @@ import qualified GHC.Generics as GHC
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
--[ GHCInfo Tree ]--
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
|
||||||
|
data GHCupInfo = GHCupInfo
|
||||||
|
{ _toolRequirements :: ToolRequirements
|
||||||
|
, _ghcupDownloads :: GHCupDownloads
|
||||||
|
}
|
||||||
|
deriving (Show, GHC.Generic)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
--[ Requirements Tree ]--
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
|
||||||
|
type ToolRequirements = Map Tool ToolReqVersionSpec
|
||||||
|
type ToolReqVersionSpec = Map (Maybe Version) PlatformReqSpec
|
||||||
|
type PlatformReqSpec = Map Platform PlatformReqVersionSpec
|
||||||
|
type PlatformReqVersionSpec = Map (Maybe Versioning) Requirements
|
||||||
|
|
||||||
|
|
||||||
|
data Requirements = Requirements
|
||||||
|
{ _distroPKGs :: [Text]
|
||||||
|
, _notes :: Text
|
||||||
|
}
|
||||||
|
deriving (Show, GHC.Generic)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
--[ Download Tree ]--
|
--[ Download Tree ]--
|
||||||
@@ -37,9 +71,10 @@ data Tool = GHC
|
|||||||
-- | All necessary information of a tool version, including
|
-- | All necessary information of a tool version, including
|
||||||
-- source download and per-architecture downloads.
|
-- source download and per-architecture downloads.
|
||||||
data VersionInfo = VersionInfo
|
data VersionInfo = VersionInfo
|
||||||
{ _viTags :: [Tag] -- ^ version specific tag
|
{ _viTags :: [Tag] -- ^ version specific tag
|
||||||
, _viSourceDL :: Maybe DownloadInfo -- ^ source tarball
|
, _viChangeLog :: Maybe URI
|
||||||
, _viArch :: ArchitectureSpec -- ^ descend for binary downloads per arch
|
, _viSourceDL :: Maybe DownloadInfo -- ^ source tarball
|
||||||
|
, _viArch :: ArchitectureSpec -- ^ descend for binary downloads per arch
|
||||||
}
|
}
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
@@ -47,7 +82,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
|
||||||
@@ -99,26 +136,31 @@ data DownloadInfo = DownloadInfo
|
|||||||
-- | Where to fetch GHCupDownloads from.
|
-- | Where to fetch GHCupDownloads from.
|
||||||
data URLSource = GHCupURL
|
data URLSource = GHCupURL
|
||||||
| OwnSource URI
|
| OwnSource URI
|
||||||
| OwnSpec GHCupDownloads
|
| OwnSpec GHCupInfo
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
data Settings = Settings
|
data Settings = Settings
|
||||||
{ cache :: Bool
|
{ cache :: Bool
|
||||||
, urlSource :: URLSource
|
, noVerify :: Bool
|
||||||
, noVerify :: Bool
|
, keepDirs :: KeepDirs
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
|
|
||||||
|
data KeepDirs = Always
|
||||||
|
| Errors
|
||||||
|
| Never
|
||||||
|
deriving (Eq, Show, Ord)
|
||||||
|
|
||||||
|
|
||||||
data DebugInfo = DebugInfo
|
data DebugInfo = DebugInfo
|
||||||
{ diBaseDir :: Path Abs
|
{ diBaseDir :: Path Abs
|
||||||
, diBinDir :: Path Abs
|
, diBinDir :: Path Abs
|
||||||
, diGHCDir :: Path Abs
|
, diGHCDir :: Path Abs
|
||||||
, diCacheDir :: Path Abs
|
, diCacheDir :: Path Abs
|
||||||
, diURLSource :: URLSource
|
, diArch :: Architecture
|
||||||
, diArch :: Architecture
|
, diPlatform :: PlatformResult
|
||||||
, diPlatform :: PlatformResult
|
|
||||||
}
|
}
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
@@ -142,3 +184,22 @@ data PlatformRequest = PlatformRequest
|
|||||||
}
|
}
|
||||||
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'
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
@@ -13,12 +14,10 @@ module GHCup.Types.JSON where
|
|||||||
|
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
import GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Aeson.TH
|
import Data.Aeson.TH
|
||||||
import Data.Aeson.Types
|
import Data.Aeson.Types
|
||||||
import Data.Text.Encoding ( decodeUtf8 )
|
|
||||||
import Data.Text.Encoding as E
|
import Data.Text.Encoding as E
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
import Data.Word8
|
import Data.Word8
|
||||||
@@ -38,12 +37,27 @@ 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 } ''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 . decodeUtf8 . serializeURIRef'
|
toJSON = toJSON . decUTF8Safe . serializeURIRef'
|
||||||
|
|
||||||
instance FromJSON URI where
|
instance FromJSON URI where
|
||||||
parseJSON = withText "URL" $ \t ->
|
parseJSON = withText "URL" $ \t ->
|
||||||
@@ -70,11 +84,11 @@ instance FromJSONKey Versioning where
|
|||||||
instance ToJSONKey (Maybe Versioning) where
|
instance ToJSONKey (Maybe Versioning) where
|
||||||
toJSONKey = toJSONKeyText $ \case
|
toJSONKey = toJSONKeyText $ \case
|
||||||
Just x -> prettyV x
|
Just x -> prettyV x
|
||||||
Nothing -> T.pack "unknown_version"
|
Nothing -> T.pack "unknown_versioning"
|
||||||
|
|
||||||
instance FromJSONKey (Maybe Versioning) where
|
instance FromJSONKey (Maybe Versioning) where
|
||||||
fromJSONKey = FromJSONKeyTextParser $ \t ->
|
fromJSONKey = FromJSONKeyTextParser $ \t ->
|
||||||
if t == T.pack "unknown_version" then pure Nothing else pure $ just t
|
if t == T.pack "unknown_versioning" then pure Nothing else pure $ just t
|
||||||
where
|
where
|
||||||
just t = case versioning t of
|
just t = case versioning t of
|
||||||
Right x -> pure x
|
Right x -> pure x
|
||||||
@@ -113,6 +127,19 @@ instance ToJSONKey Architecture where
|
|||||||
instance FromJSONKey Architecture where
|
instance FromJSONKey Architecture where
|
||||||
fromJSONKey = genericFromJSONKey defaultJSONKeyOptions
|
fromJSONKey = genericFromJSONKey defaultJSONKeyOptions
|
||||||
|
|
||||||
|
instance ToJSONKey (Maybe Version) where
|
||||||
|
toJSONKey = toJSONKeyText $ \case
|
||||||
|
Just x -> prettyVer x
|
||||||
|
Nothing -> T.pack "unknown_version"
|
||||||
|
|
||||||
|
instance FromJSONKey (Maybe Version) where
|
||||||
|
fromJSONKey = FromJSONKeyTextParser $ \t ->
|
||||||
|
if t == T.pack "unknown_version" then pure Nothing else pure $ just t
|
||||||
|
where
|
||||||
|
just t = case version t of
|
||||||
|
Right x -> pure x
|
||||||
|
Left e -> fail $ "Failure in (Maybe Version) (FromJSONKey)" <> show e
|
||||||
|
|
||||||
instance ToJSON Version where
|
instance ToJSON Version where
|
||||||
toJSON = toJSON . prettyVer
|
toJSON = toJSON . prettyVer
|
||||||
|
|
||||||
@@ -129,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
|
||||||
|
|
||||||
@@ -137,8 +172,8 @@ instance FromJSONKey Tool where
|
|||||||
|
|
||||||
instance ToJSON (Path Rel) where
|
instance ToJSON (Path Rel) where
|
||||||
toJSON p = case and . fmap isAscii . BS.unpack $ fp of
|
toJSON p = case and . fmap isAscii . BS.unpack $ fp of
|
||||||
True -> toJSON . E.decodeUtf8 $ fp
|
True -> toJSON . decUTF8Safe $ fp
|
||||||
False -> String [s|/not/a/valid/path|]
|
False -> String "/not/a/valid/path"
|
||||||
where fp = toFilePath p
|
where fp = toFilePath p
|
||||||
|
|
||||||
instance FromJSON (Path Rel) where
|
instance FromJSON (Path Rel) where
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ makeLenses ''DownloadInfo
|
|||||||
makeLenses ''Tag
|
makeLenses ''Tag
|
||||||
makeLenses ''VersionInfo
|
makeLenses ''VersionInfo
|
||||||
|
|
||||||
|
makeLenses ''GHCTargetVersion
|
||||||
|
|
||||||
|
makeLenses ''GHCupInfo
|
||||||
|
|
||||||
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
uriSchemeL' :: Lens' (URIRef Absolute) Scheme
|
||||||
uriSchemeL' = lensVL uriSchemeL
|
uriSchemeL' = lensVL uriSchemeL
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
|
|
||||||
module GHCup.Utils
|
module GHCup.Utils
|
||||||
@@ -13,24 +16,28 @@ where
|
|||||||
|
|
||||||
import GHCup.Errors
|
import GHCup.Errors
|
||||||
import GHCup.Types
|
import GHCup.Types
|
||||||
|
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 GHCup.Utils.String.QQ
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
import Control.Monad.Fail ( MonadFail )
|
import Control.Monad.Fail ( MonadFail )
|
||||||
|
#endif
|
||||||
import Control.Monad.Logger
|
import Control.Monad.Logger
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Control.Monad.Trans.Class ( lift )
|
|
||||||
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
|
||||||
@@ -44,8 +51,11 @@ import Prelude hiding ( abs
|
|||||||
)
|
)
|
||||||
import Safe
|
import Safe
|
||||||
import System.IO.Error
|
import System.IO.Error
|
||||||
import System.Posix.FilePath ( takeFileName )
|
import System.Posix.FilePath ( getSearchPath
|
||||||
|
, takeFileName
|
||||||
|
)
|
||||||
import System.Posix.Files.ByteString ( readSymbolicLink )
|
import System.Posix.Files.ByteString ( readSymbolicLink )
|
||||||
|
import Text.Regex.Posix
|
||||||
import URI.ByteString
|
import URI.ByteString
|
||||||
|
|
||||||
import qualified Codec.Archive.Tar as Tar
|
import qualified Codec.Archive.Tar as Tar
|
||||||
@@ -55,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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -68,62 +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 = [s|../ghc/|] <> verToBS ver <> [s|/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 [s|../ghc/|] *> verParser <* string [s|/bin/ghc|]
|
|
||||||
verParser = many1' (notWord8 _slash) >>= \t ->
|
|
||||||
case version $ E.decodeUtf8 $ 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 -> ([s|-|] <> 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 <> [s|.|] <> 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 -> ([s|-|] <> 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
|
||||||
@@ -136,33 +153,60 @@ 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, MonadThrow m) => m (Maybe Version)
|
ghcSet :: (MonadThrow m, MonadIO m)
|
||||||
ghcSet = do
|
=> Maybe Text -- ^ the target of the GHC version, if any
|
||||||
ghcBin <- (</> ([rel|ghc|] :: Path Rel)) <$> 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
|
||||||
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 "/bin/"
|
||||||
|
<* ghcTargetBinP "ghc"
|
||||||
|
<* 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
|
||||||
@@ -172,10 +216,10 @@ cabalInstalled ver = do
|
|||||||
|
|
||||||
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
cabalSet :: (MonadIO m, MonadThrow m) => m Version
|
||||||
cabalSet = do
|
cabalSet = do
|
||||||
cabalbin <- (</> ([rel|cabal|] :: Path Rel)) <$> liftIO ghcupBinDir
|
cabalbin <- (</> [rel|cabal|]) <$> liftIO ghcupBinDir
|
||||||
mc <- liftIO $ executeOut cabalbin [[s|--numeric-version|]] Nothing
|
mc <- liftIO $ executeOut cabalbin ["--numeric-version"] Nothing
|
||||||
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ mc
|
let reportedVer = fst . B.spanEnd (== _lf) . _stdOut $ mc
|
||||||
case version (E.decodeUtf8 reportedVer) of
|
case version $ decUTF8Safe reportedVer of
|
||||||
Left e -> throwM e
|
Left e -> throwM e
|
||||||
Right r -> pure r
|
Right r -> pure r
|
||||||
|
|
||||||
@@ -186,32 +230,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 $ throwEither . semver . E.decodeUtf8 . toFilePath
|
|
||||||
mapM (throwEither . version)
|
pure
|
||||||
. 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -228,22 +289,23 @@ unpackToDir :: (MonadLogger m, MonadIO m, MonadThrow m)
|
|||||||
-> Path Abs -- ^ archive path
|
-> Path Abs -- ^ archive path
|
||||||
-> Excepts '[UnknownArchive] m ()
|
-> Excepts '[UnknownArchive] m ()
|
||||||
unpackToDir dest av = do
|
unpackToDir dest av = do
|
||||||
let fp = E.decodeUtf8 (toFilePath av)
|
fp <- (decUTF8Safe . toFilePath) <$> basename av
|
||||||
lift $ $(logInfo) [i|Unpacking: #{fp}|]
|
let dfp = decUTF8Safe . toFilePath $ dest
|
||||||
|
lift $ $(logInfo) [i|Unpacking: #{fp} to #{dfp}|]
|
||||||
fn <- toFilePath <$> basename av
|
fn <- toFilePath <$> basename av
|
||||||
let untar = Tar.unpack (toFilePath dest) . Tar.read
|
let untar = Tar.unpack (toFilePath dest) . Tar.read
|
||||||
|
|
||||||
-- extract, depending on file extension
|
-- extract, depending on file extension
|
||||||
if
|
if
|
||||||
| [s|.tar.gz|] `B.isSuffixOf` fn -> liftIO
|
| ".tar.gz" `B.isSuffixOf` fn -> liftIO
|
||||||
(untar . GZip.decompress =<< readFile av)
|
(untar . GZip.decompress =<< readFile av)
|
||||||
| [s|.tar.xz|] `B.isSuffixOf` fn -> do
|
| ".tar.xz" `B.isSuffixOf` fn -> do
|
||||||
filecontents <- liftIO $ readFile av
|
filecontents <- liftIO $ readFile av
|
||||||
let decompressed = Lzma.decompress filecontents
|
let decompressed = Lzma.decompress filecontents
|
||||||
liftIO $ untar decompressed
|
liftIO $ untar decompressed
|
||||||
| [s|.tar.bz2|] `B.isSuffixOf` fn -> liftIO
|
| ".tar.bz2" `B.isSuffixOf` fn -> liftIO
|
||||||
(untar . BZip.decompress =<< readFile av)
|
(untar . BZip.decompress =<< readFile av)
|
||||||
| [s|.tar|] `B.isSuffixOf` fn -> liftIO (untar =<< readFile av)
|
| ".tar" `B.isSuffixOf` fn -> liftIO (untar =<< readFile av)
|
||||||
| otherwise -> throwE $ UnknownArchive fn
|
| otherwise -> throwE $ UnknownArchive fn
|
||||||
|
|
||||||
|
|
||||||
@@ -254,21 +316,27 @@ unpackToDir dest av = do
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
||||||
-- | Get the tool versions that have this tag.
|
-- | Get the tool version that has this tag. If multiple have it,
|
||||||
getTagged :: GHCupDownloads -> Tool -> Tag -> [Version]
|
-- picks the greatest version.
|
||||||
getTagged av tool tag = toListOf
|
getTagged :: Tag
|
||||||
( ix tool
|
-> AffineFold (Map.Map Version VersionInfo) (Version, VersionInfo)
|
||||||
% to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
getTagged tag =
|
||||||
% to Map.keys
|
( to (Map.filter (\VersionInfo {..} -> elem tag _viTags))
|
||||||
% folded
|
% to Map.toDescList
|
||||||
|
% _head
|
||||||
)
|
)
|
||||||
av
|
|
||||||
|
|
||||||
getLatest :: GHCupDownloads -> Tool -> Maybe Version
|
getLatest :: GHCupDownloads -> Tool -> Maybe Version
|
||||||
getLatest av tool = headOf folded $ getTagged av tool Latest
|
getLatest av tool = headOf (ix tool % getTagged Latest % to fst) $ av
|
||||||
|
|
||||||
getRecommended :: GHCupDownloads -> Tool -> Maybe Version
|
getRecommended :: GHCupDownloads -> Tool -> Maybe Version
|
||||||
getRecommended av tool = headOf folded $ getTagged av tool Recommended
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -277,9 +345,6 @@ getRecommended av tool = headOf folded $ getTagged av tool Recommended
|
|||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
|
||||||
getUrlSource :: MonadReader Settings m => m URLSource
|
|
||||||
getUrlSource = ask <&> urlSource
|
|
||||||
|
|
||||||
getCache :: MonadReader Settings m => m Bool
|
getCache :: MonadReader Settings m => m Bool
|
||||||
getCache = ask <&> cache
|
getCache = ask <&> cache
|
||||||
|
|
||||||
@@ -296,13 +361,13 @@ urlBaseName :: MonadThrow m
|
|||||||
urlBaseName = parseRel . snd . B.breakEnd (== _slash) . urlDecode False
|
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
|
||||||
@@ -310,21 +375,114 @@ 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 [s|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
|
||||||
-- this GHC was built from source. It contains the build config.
|
-- this GHC was built from source. It contains the build config.
|
||||||
ghcUpSrcBuiltFile :: Path Rel
|
ghcUpSrcBuiltFile :: Path Rel
|
||||||
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
ghcUpSrcBuiltFile = [rel|.ghcup_src_built|]
|
||||||
|
|
||||||
|
|
||||||
|
-- | Calls gmake if it exists in PATH, otherwise make.
|
||||||
|
make :: [ByteString] -> Maybe (Path Abs) -> IO (Either ProcessError ())
|
||||||
|
make args workdir = do
|
||||||
|
spaths <- catMaybes . fmap parseAbs <$> getSearchPath
|
||||||
|
has_gmake <- isJust <$> searchPath spaths [rel|gmake|]
|
||||||
|
let mymake = if has_gmake then "gmake" else "make"
|
||||||
|
execLogged mymake True args [rel|ghc-make|] workdir Nothing
|
||||||
|
|
||||||
|
|
||||||
|
-- | Try to apply patches in order. Fails with 'PatchFailed'
|
||||||
|
-- on first failure.
|
||||||
|
applyPatches :: (MonadLogger m, MonadIO m)
|
||||||
|
=> Path Abs -- ^ dir containing patches
|
||||||
|
-> Path Abs -- ^ dir to apply patches in
|
||||||
|
-> Excepts '[PatchFailed] m ()
|
||||||
|
applyPatches pdir ddir = do
|
||||||
|
patches <- liftIO $ getDirsFiles pdir
|
||||||
|
forM_ (sort patches) $ \patch' -> do
|
||||||
|
lift $ $(logInfo) [i|Applying patch #{patch'}|]
|
||||||
|
(fmap (either (const Nothing) Just) $ liftIO $ exec
|
||||||
|
"patch"
|
||||||
|
True
|
||||||
|
["-p1", "-i", toFilePath patch']
|
||||||
|
(Just ddir)
|
||||||
|
Nothing
|
||||||
|
)
|
||||||
|
!? PatchFailed
|
||||||
|
|
||||||
|
|
||||||
|
darwinNotarization :: Platform -> Path Abs -> IO (Either ProcessError ())
|
||||||
|
darwinNotarization Darwin path = exec
|
||||||
|
"xattr"
|
||||||
|
True
|
||||||
|
["-r", "-d", "com.apple.quarantine", toFilePath path]
|
||||||
|
Nothing
|
||||||
|
Nothing
|
||||||
|
darwinNotarization _ _ = pure $ Right ()
|
||||||
|
|
||||||
|
|
||||||
|
getChangeLog :: GHCupDownloads -> Tool -> Either Version Tag -> Maybe URI
|
||||||
|
getChangeLog dls tool (Left v') =
|
||||||
|
preview (ix tool % ix v' % viChangeLog % _Just) dls
|
||||||
|
getChangeLog dls tool (Right tag) =
|
||||||
|
preview (ix tool % getTagged tag % to snd % viChangeLog % _Just) dls
|
||||||
|
|
||||||
|
|
||||||
|
-- | Execute a build action while potentially cleaning up:
|
||||||
|
--
|
||||||
|
-- 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,11 +1,14 @@
|
|||||||
|
{-# 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 GHCup.Utils.String.QQ
|
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
@@ -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,38 +41,52 @@ import qualified System.Posix.User as PU
|
|||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
||||||
|
-- | ~/.ghcup by default
|
||||||
ghcupBaseDir :: IO (Path Abs)
|
ghcupBaseDir :: IO (Path Abs)
|
||||||
ghcupBaseDir = do
|
ghcupBaseDir = do
|
||||||
getEnv [s|GHCUP_INSTALL_BASE_PREFIX|] >>= \case
|
bdir <- getEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
|
||||||
Just r -> parseAbs r
|
Just r -> parseAbs r
|
||||||
Nothing -> do
|
Nothing -> liftIO getHomeDirectory
|
||||||
home <- liftIO getHomeDirectory
|
pure (bdir </> [rel|.ghcup|])
|
||||||
pure (home </> ([rel|.ghcup|] :: Path Rel))
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | ~/.ghcup/ghc by default.
|
||||||
ghcupGHCBaseDir :: IO (Path Abs)
|
ghcupGHCBaseDir :: IO (Path Abs)
|
||||||
ghcupGHCBaseDir = ghcupBaseDir <&> (</> ([rel|ghc|] :: Path Rel))
|
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|] :: Path Rel))
|
ghcupBinDir = ghcupBaseDir <&> (</> [rel|bin|])
|
||||||
|
|
||||||
ghcupCacheDir :: IO (Path Abs)
|
ghcupCacheDir :: IO (Path Abs)
|
||||||
ghcupCacheDir = ghcupBaseDir <&> (</> ([rel|cache|] :: Path Rel))
|
ghcupCacheDir = ghcupBaseDir <&> (</> [rel|cache|])
|
||||||
|
|
||||||
ghcupLogsDir :: IO (Path Abs)
|
ghcupLogsDir :: IO (Path Abs)
|
||||||
ghcupLogsDir = ghcupBaseDir <&> (</> ([rel|logs|] :: Path Rel))
|
ghcupLogsDir = ghcupBaseDir <&> (</> [rel|logs|])
|
||||||
|
|
||||||
|
|
||||||
mkGhcupTmpDir :: (MonadThrow m, MonadIO m) => m (Path Abs)
|
mkGhcupTmpDir :: (MonadThrow m, MonadIO m) => m (Path Abs)
|
||||||
mkGhcupTmpDir = do
|
mkGhcupTmpDir = do
|
||||||
tmpdir <- liftIO $ getEnvDefault [s|TMPDIR|] [s|/tmp|]
|
tmpdir <- liftIO $ getEnvDefault "TMPDIR" "/tmp"
|
||||||
tmp <- liftIO $ mkdtemp $ (tmpdir FP.</> [s|ghcup-|])
|
tmp <- liftIO $ mkdtemp $ (tmpdir FP.</> "ghcup-")
|
||||||
parseAbs tmp
|
parseAbs tmp
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +101,7 @@ withGHCupTmpDir = snd <$> allocate mkGhcupTmpDir deleteDirRecursive
|
|||||||
|
|
||||||
getHomeDirectory :: IO (Path Abs)
|
getHomeDirectory :: IO (Path Abs)
|
||||||
getHomeDirectory = do
|
getHomeDirectory = do
|
||||||
e <- getEnv [s|HOME|]
|
e <- getEnv "HOME"
|
||||||
case e of
|
case e of
|
||||||
Just fp -> parseAbs fp
|
Just fp -> parseAbs fp
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
{-# LANGUAGE QuasiQuotes #-}
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
|
||||||
module GHCup.Utils.File where
|
module GHCup.Utils.File where
|
||||||
|
|
||||||
import GHCup.Utils.Dirs
|
import GHCup.Utils.Dirs
|
||||||
import GHCup.Utils.Prelude
|
import GHCup.Utils.Prelude
|
||||||
|
|
||||||
|
import Control.Concurrent
|
||||||
|
import Control.Exception ( evaluate )
|
||||||
import Control.Exception.Safe
|
import Control.Exception.Safe
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Data.ByteString
|
import Data.ByteString ( ByteString )
|
||||||
import Data.ByteString.Unsafe ( unsafeUseAsCStringLen )
|
import Data.ByteString.Unsafe ( unsafeUseAsCStringLen )
|
||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Functor
|
import Data.Functor
|
||||||
|
import Data.IORef
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.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
|
||||||
@@ -23,7 +29,10 @@ import Optics
|
|||||||
import Streamly
|
import Streamly
|
||||||
import Streamly.External.ByteString
|
import Streamly.External.ByteString
|
||||||
import Streamly.External.ByteString.Lazy
|
import Streamly.External.ByteString.Lazy
|
||||||
|
import System.Console.Pretty
|
||||||
|
import System.Console.Regions
|
||||||
import System.IO
|
import System.IO
|
||||||
|
import System.IO.Error
|
||||||
import System.Posix.Directory.ByteString
|
import System.Posix.Directory.ByteString
|
||||||
import System.Posix.FD as FD
|
import System.Posix.FD as FD
|
||||||
import System.Posix.FilePath hiding ( (</>) )
|
import System.Posix.FilePath hiding ( (</>) )
|
||||||
@@ -32,8 +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 Data.Text as T
|
||||||
|
import qualified Data.Text.Encoding as E
|
||||||
import qualified System.Posix.Process.ByteString
|
import qualified System.Posix.Process.ByteString
|
||||||
as SPPB
|
as SPPB
|
||||||
import Streamly.External.Posix.DirStream
|
import Streamly.External.Posix.DirStream
|
||||||
@@ -42,7 +55,19 @@ 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.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
|
import qualified "unix-bytestring" System.Posix.IO.ByteString
|
||||||
|
as SPIB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- | Bool signals whether the regions should be cleaned.
|
||||||
|
data StopThread = StopThread Bool
|
||||||
|
deriving Show
|
||||||
|
|
||||||
|
instance Exception StopThread
|
||||||
|
|
||||||
|
|
||||||
data ProcessError = NonZeroExit Int ByteString [ByteString]
|
data ProcessError = NonZeroExit Int ByteString [ByteString]
|
||||||
@@ -99,7 +124,7 @@ findExecutable ex = do
|
|||||||
|
|
||||||
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
-- | Execute the given command and collect the stdout, stderr and the exit code.
|
||||||
-- The command is run in a subprocess.
|
-- The command is run in a subprocess.
|
||||||
executeOut :: Path b -- ^ command as filename, e.g. 'ls'
|
executeOut :: Path b -- ^ command as filename, e.g. 'ls'
|
||||||
-> [ByteString] -- ^ arguments to the command
|
-> [ByteString] -- ^ arguments to the command
|
||||||
-> Maybe (Path Abs) -- ^ chdir to this path
|
-> Maybe (Path Abs) -- ^ chdir to this path
|
||||||
-> IO CapturedProcess
|
-> IO CapturedProcess
|
||||||
@@ -116,26 +141,102 @@ execLogged :: ByteString -- ^ thing to execute
|
|||||||
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
-> Maybe [(ByteString, ByteString)] -- ^ optional environment
|
||||||
-> IO (Either ProcessError ())
|
-> IO (Either ProcessError ())
|
||||||
execLogged exe spath args lfile chdir env = do
|
execLogged exe spath args lfile chdir env = do
|
||||||
ldir <- ghcupLogsDir
|
ldir <- ghcupLogsDir
|
||||||
let logfile = ldir </> lfile
|
logfile <- (ldir </>) <$> parseRel (toFilePath lfile <> ".log")
|
||||||
bracket (createFile (toFilePath logfile) newFilePerms) closeFd action
|
bracket (createFile (toFilePath logfile) newFilePerms) closeFd action
|
||||||
where
|
where
|
||||||
action fd = do
|
action fd = do
|
||||||
pid <- SPPB.forkProcess $ do
|
actionWithPipes $ \(stdoutRead, stdoutWrite) -> do
|
||||||
-- dup stdout
|
-- start the thread that logs to stdout in a region
|
||||||
void $ dupTo fd stdOutput
|
done <- newEmptyMVar
|
||||||
|
tid <-
|
||||||
|
forkIO
|
||||||
|
$ EX.handle (\(_ :: StopThread) -> pure ())
|
||||||
|
$ EX.handle (\(_ :: IOException) -> pure ())
|
||||||
|
$ flip finally (putMVar done ())
|
||||||
|
$ printToRegion fd stdoutRead 6
|
||||||
|
|
||||||
-- dup stderr
|
-- fork our subprocess
|
||||||
void $ dupTo fd stdError
|
pid <- SPPB.forkProcess $ do
|
||||||
|
void $ dupTo stdoutWrite stdOutput
|
||||||
|
void $ dupTo stdoutWrite stdError
|
||||||
|
closeFd stdoutWrite
|
||||||
|
closeFd stdoutRead
|
||||||
|
|
||||||
-- execute the action
|
-- execute the action
|
||||||
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
maybe (pure ()) (changeWorkingDirectory . toFilePath) chdir
|
||||||
SPPB.executeFile exe spath args env
|
SPPB.executeFile exe spath args env
|
||||||
|
|
||||||
|
closeFd stdoutWrite
|
||||||
|
|
||||||
SPPB.getProcessStatus True True pid >>= \case
|
-- wait for the subprocess to finish
|
||||||
i@(Just (SPPB.Exited _)) -> pure $ toProcessError exe args i
|
e <- SPPB.getProcessStatus True True pid >>= \case
|
||||||
i -> pure $ toProcessError exe args i
|
i@(Just (SPPB.Exited _)) -> pure $ toProcessError exe args i
|
||||||
|
i -> pure $ toProcessError exe args i
|
||||||
|
|
||||||
|
-- make sure the logging thread stops
|
||||||
|
case e of
|
||||||
|
Left _ -> EX.throwTo tid (StopThread False)
|
||||||
|
Right _ -> EX.throwTo tid (StopThread True)
|
||||||
|
takeMVar done
|
||||||
|
|
||||||
|
closeFd stdoutRead
|
||||||
|
pure e
|
||||||
|
|
||||||
|
-- Reads fdIn and logs the output in a continous scrolling area
|
||||||
|
-- of 'size' terminal lines. Also writes to a log file.
|
||||||
|
printToRegion fileFd fdIn size = do
|
||||||
|
ref <- newIORef ([] :: [ByteString])
|
||||||
|
displayConsoleRegions $ do
|
||||||
|
rs <- sequence . replicate size . openConsoleRegion $ Linear
|
||||||
|
flip finally (readTilEOF (lineAction ref rs) fdIn) -- make sure the last few lines don't get cut off
|
||||||
|
$ handle
|
||||||
|
(\(StopThread b) -> do
|
||||||
|
when b (forM_ rs closeConsoleRegion)
|
||||||
|
EX.throw (StopThread b)
|
||||||
|
)
|
||||||
|
$ do
|
||||||
|
hideError eofErrorType $ readTilEOF (lineAction ref rs) fdIn
|
||||||
|
-- wait for explicit stop from the parent to signal what cleanup to run
|
||||||
|
forever (threadDelay 5000)
|
||||||
|
|
||||||
|
where
|
||||||
|
-- action to perform line by line
|
||||||
|
lineAction ref rs bs' = do
|
||||||
|
modifyIORef' ref (swapRegs bs')
|
||||||
|
regs <- readIORef ref
|
||||||
|
void $ SPIB.fdWrite fileFd (bs' <> "\n")
|
||||||
|
forM (zip regs rs) $ \(bs, r) -> do
|
||||||
|
setConsoleRegion r $ do
|
||||||
|
w <- consoleWidth
|
||||||
|
return
|
||||||
|
. T.pack
|
||||||
|
. color Blue
|
||||||
|
. T.unpack
|
||||||
|
. decUTF8Safe
|
||||||
|
. trim w
|
||||||
|
. (\b -> "[ " <> toFilePath lfile <> " ] " <> b)
|
||||||
|
$ bs
|
||||||
|
|
||||||
|
swapRegs bs regs | length regs < size = regs ++ [bs]
|
||||||
|
| otherwise = tail regs ++ [bs]
|
||||||
|
|
||||||
|
-- trim output line to terminal width
|
||||||
|
trim w bs | BS.length bs > w && w > 5 = BS.take (w - 4) bs <> "..."
|
||||||
|
| otherwise = bs
|
||||||
|
|
||||||
|
-- read an entire line from the file descriptor (removes the newline char)
|
||||||
|
readLine fd' = do
|
||||||
|
bs <- SPIB.fdRead fd' 1
|
||||||
|
if
|
||||||
|
| bs == "\n" -> pure ""
|
||||||
|
| bs == "" -> pure ""
|
||||||
|
| otherwise -> fmap (bs <>) $ readLine fd'
|
||||||
|
|
||||||
|
readTilEOF action' fd' = do
|
||||||
|
bs <- readLine fd'
|
||||||
|
void $ action' bs
|
||||||
|
readTilEOF action' fd'
|
||||||
|
|
||||||
|
|
||||||
-- | Capture the stdout and stderr of the given action, which
|
-- | Capture the stdout and stderr of the given action, which
|
||||||
@@ -144,7 +245,7 @@ execLogged exe spath args lfile chdir env = do
|
|||||||
captureOutStreams :: IO a
|
captureOutStreams :: IO a
|
||||||
-- ^ the action to execute in a subprocess
|
-- ^ the action to execute in a subprocess
|
||||||
-> IO CapturedProcess
|
-> IO CapturedProcess
|
||||||
captureOutStreams action =
|
captureOutStreams action = do
|
||||||
actionWithPipes $ \(parentStdoutRead, childStdoutWrite) ->
|
actionWithPipes $ \(parentStdoutRead, childStdoutWrite) ->
|
||||||
actionWithPipes $ \(parentStderrRead, childStderrWrite) -> do
|
actionWithPipes $ \(parentStderrRead, childStderrWrite) -> do
|
||||||
pid <- SPPB.forkProcess $ do
|
pid <- SPPB.forkProcess $ do
|
||||||
@@ -159,27 +260,68 @@ captureOutStreams action =
|
|||||||
closeFd parentStderrRead
|
closeFd parentStderrRead
|
||||||
|
|
||||||
-- execute the action
|
-- execute the action
|
||||||
void $ action
|
a <- action
|
||||||
|
void $ evaluate a
|
||||||
|
|
||||||
-- close everything we don't need
|
-- close everything we don't need
|
||||||
closeFd childStdoutWrite
|
closeFd childStdoutWrite
|
||||||
closeFd childStderrWrite
|
closeFd childStderrWrite
|
||||||
|
|
||||||
SPPB.getProcessStatus True True pid >>= \case
|
-- start thread that writes the output
|
||||||
|
refOut <- newIORef BS.empty
|
||||||
|
refErr <- newIORef BS.empty
|
||||||
|
done <- newEmptyMVar
|
||||||
|
_ <-
|
||||||
|
forkIO
|
||||||
|
$ EX.handle (\(_ :: StopThread) -> pure ())
|
||||||
|
$ EX.handle (\(_ :: IOException) -> pure ())
|
||||||
|
$ flip finally (putMVar done ())
|
||||||
|
$ writeStds parentStdoutRead parentStderrRead refOut refErr
|
||||||
|
|
||||||
|
status <- SPPB.getProcessStatus True True pid
|
||||||
|
takeMVar done
|
||||||
|
|
||||||
|
case status of
|
||||||
-- readFd will take care of closing the fd
|
-- readFd will take care of closing the fd
|
||||||
Just (SPPB.Exited es) -> do
|
Just (SPPB.Exited es) -> do
|
||||||
stdout' <- L.toStrict <$> readFd parentStdoutRead
|
stdout' <- readIORef refOut
|
||||||
stderr' <- L.toStrict <$> readFd parentStderrRead
|
stderr' <- readIORef refErr
|
||||||
pure $ CapturedProcess { _exitCode = es
|
pure $ CapturedProcess { _exitCode = es
|
||||||
, _stdOut = stdout'
|
, _stdOut = stdout'
|
||||||
, _stdErr = stderr'
|
, _stdErr = stderr'
|
||||||
}
|
}
|
||||||
|
|
||||||
_ -> throwIO $ userError $ ("No such PID " ++ show pid)
|
_ -> throwIO $ userError $ ("No such PID " ++ show pid)
|
||||||
|
|
||||||
where
|
where
|
||||||
actionWithPipes a =
|
writeStds pout perr rout rerr = do
|
||||||
createPipe >>= \(p1, p2) -> (flip finally) (cleanup [p1, p2]) $ a (p1, p2)
|
doneOut <- newEmptyMVar
|
||||||
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
void
|
||||||
|
$ forkIO
|
||||||
|
$ hideError eofErrorType
|
||||||
|
$ flip finally (putMVar doneOut ())
|
||||||
|
$ readTilEOF (\x -> modifyIORef' rout (<> x)) pout
|
||||||
|
doneErr <- newEmptyMVar
|
||||||
|
void
|
||||||
|
$ forkIO
|
||||||
|
$ hideError eofErrorType
|
||||||
|
$ flip finally (putMVar doneErr ())
|
||||||
|
$ readTilEOF (\x -> modifyIORef' rerr (<> x)) perr
|
||||||
|
takeMVar doneOut
|
||||||
|
takeMVar doneErr
|
||||||
|
|
||||||
|
readTilEOF ~action' fd' = do
|
||||||
|
bs <- SPIB.fdRead fd' 512
|
||||||
|
void $ action' bs
|
||||||
|
readTilEOF action' fd'
|
||||||
|
|
||||||
|
|
||||||
|
actionWithPipes :: ((Fd, Fd) -> IO b) -> IO b
|
||||||
|
actionWithPipes a =
|
||||||
|
createPipe >>= \(p1, p2) -> (flip finally) (cleanup [p1, p2]) $ a (p1, p2)
|
||||||
|
|
||||||
|
cleanup :: [Fd] -> IO ()
|
||||||
|
cleanup fds = for_ fds $ \fd -> handleIO (\_ -> pure ()) $ closeFd fd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -232,8 +374,7 @@ searchPath paths needle = go paths
|
|||||||
where
|
where
|
||||||
go [] = pure Nothing
|
go [] = pure Nothing
|
||||||
go (x : xs) =
|
go (x : xs) =
|
||||||
hideErrorDefM PermissionDenied (go xs)
|
hideErrorDefM [InappropriateType, PermissionDenied, NoSuchThing] (go xs)
|
||||||
$ hideErrorDefM NoSuchThing (go xs)
|
|
||||||
$ do
|
$ do
|
||||||
dirStream <- openDirStream (toFilePath x)
|
dirStream <- openDirStream (toFilePath x)
|
||||||
S.findM (\(_, p) -> isMatch x p) (dirContentsStream dirStream)
|
S.findM (\(_, p) -> isMatch x p) (dirContentsStream dirStream)
|
||||||
@@ -244,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"
|
||||||
@@ -17,7 +17,6 @@ import Control.Monad.IO.Class
|
|||||||
import Control.Monad.Trans.Class ( lift )
|
import Control.Monad.Trans.Class ( lift )
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.ByteString ( ByteString )
|
import Data.ByteString ( ByteString )
|
||||||
import Data.Monoid ( (<>) )
|
|
||||||
import Data.String
|
import Data.String
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
@@ -30,6 +29,7 @@ import qualified Data.ByteString.Lazy as L
|
|||||||
import qualified Data.Strict.Maybe as S
|
import qualified Data.Strict.Maybe as S
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
|
import qualified Data.Text.Encoding.Error as E
|
||||||
import qualified Data.Text.Lazy as TL
|
import qualified Data.Text.Lazy as TL
|
||||||
import qualified Data.Text.Lazy.Builder as B
|
import qualified Data.Text.Lazy.Builder as B
|
||||||
import qualified Data.Text.Lazy.Builder.Int as B
|
import qualified Data.Text.Lazy.Builder.Int as B
|
||||||
@@ -88,10 +88,6 @@ whileM_ ~action = void . whileM action
|
|||||||
guardM :: (Monad m, Alternative m) => m Bool -> m ()
|
guardM :: (Monad m, Alternative m) => m Bool -> m ()
|
||||||
guardM ~f = guard =<< f
|
guardM ~f = guard =<< f
|
||||||
|
|
||||||
lBS2sT :: L.ByteString -> Text
|
|
||||||
lBS2sT = TL.toStrict . TLE.decodeUtf8
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleIO' :: (MonadIO m, MonadCatch m)
|
handleIO' :: (MonadIO m, MonadCatch m)
|
||||||
=> IOErrorType
|
=> IOErrorType
|
||||||
@@ -169,14 +165,14 @@ liftIOException errType ex =
|
|||||||
. lift
|
. lift
|
||||||
|
|
||||||
|
|
||||||
hideErrorDef :: IOErrorType -> a -> IO a -> IO a
|
hideErrorDef :: [IOErrorType] -> a -> IO a -> IO a
|
||||||
hideErrorDef err def =
|
hideErrorDef errs def =
|
||||||
handleIO (\e -> if err == ioeGetErrorType e then pure def else ioError e)
|
handleIO (\e -> if ioeGetErrorType e `elem` errs then pure def else ioError e)
|
||||||
|
|
||||||
|
|
||||||
hideErrorDefM :: IOErrorType -> IO a -> IO a -> IO a
|
hideErrorDefM :: [IOErrorType] -> IO a -> IO a -> IO a
|
||||||
hideErrorDefM err def =
|
hideErrorDefM errs def =
|
||||||
handleIO (\e -> if err == ioeGetErrorType e then def else ioError e)
|
handleIO (\e -> if ioeGetErrorType e `elem` errs then def else ioError e)
|
||||||
|
|
||||||
|
|
||||||
-- TODO: does this work?
|
-- TODO: does this work?
|
||||||
@@ -222,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
|
||||||
|
|
||||||
@@ -241,3 +243,19 @@ addToCurrentEnv :: MonadIO m
|
|||||||
addToCurrentEnv adds = do
|
addToCurrentEnv adds = do
|
||||||
cEnv <- liftIO $ getEnvironment
|
cEnv <- liftIO $ getEnvironment
|
||||||
pure (adds ++ cEnv)
|
pure (adds ++ cEnv)
|
||||||
|
|
||||||
|
|
||||||
|
pvpToVersion :: PVP -> Version
|
||||||
|
pvpToVersion =
|
||||||
|
either (\_ -> error "Couldn't convert PVP to Version") id
|
||||||
|
. version
|
||||||
|
. prettyPVP
|
||||||
|
|
||||||
|
|
||||||
|
-- | Safe 'decodeUtf8With'. Replaces an invalid input byte with
|
||||||
|
-- the Unicode replacement character U+FFFD.
|
||||||
|
decUTF8Safe :: ByteString -> Text
|
||||||
|
decUTF8Safe = E.decodeUtf8With E.lenientDecode
|
||||||
|
|
||||||
|
decUTF8Safe' :: L.ByteString -> Text
|
||||||
|
decUTF8Safe' = TL.toStrict . TLE.decodeUtf8With E.lenientDecode
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE DeriveLift #-}
|
{-# LANGUAGE DeriveLift #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
@@ -11,11 +12,12 @@ module GHCup.Utils.Version.QQ where
|
|||||||
import Data.Data
|
import Data.Data
|
||||||
import Data.Text ( Text )
|
import Data.Text ( Text )
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
import GHC.Base
|
import GHC.Base
|
||||||
|
#endif
|
||||||
import Language.Haskell.TH
|
import Language.Haskell.TH
|
||||||
import Language.Haskell.TH.Quote ( QuasiQuoter(..) )
|
import Language.Haskell.TH.Quote ( QuasiQuoter(..) )
|
||||||
import Language.Haskell.TH.Syntax ( Exp(..)
|
import Language.Haskell.TH.Syntax ( Lift
|
||||||
, Lift
|
|
||||||
, dataToExpQ
|
, dataToExpQ
|
||||||
)
|
)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
@@ -33,12 +35,15 @@ deriving instance Data Mess
|
|||||||
deriving instance Lift Mess
|
deriving instance Lift Mess
|
||||||
deriving instance Data PVP
|
deriving instance Data PVP
|
||||||
deriving instance Lift PVP
|
deriving instance Lift PVP
|
||||||
deriving instance Lift (NonEmpty Word)
|
|
||||||
deriving instance Lift VSep
|
deriving instance Lift VSep
|
||||||
deriving instance Data VSep
|
deriving instance Data VSep
|
||||||
deriving instance Lift VUnit
|
deriving instance Lift VUnit
|
||||||
deriving instance Data VUnit
|
deriving instance Data VUnit
|
||||||
|
|
||||||
|
#if !MIN_VERSION_base(4,13,0)
|
||||||
|
deriving instance Lift (NonEmpty Word)
|
||||||
instance Lift Text
|
instance Lift Text
|
||||||
|
#endif
|
||||||
|
|
||||||
qq :: (Text -> Q Exp) -> QuasiQuoter
|
qq :: (Text -> Q Exp) -> QuasiQuoter
|
||||||
qq quoteExp' = QuasiQuoter
|
qq quoteExp' = QuasiQuoter
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ module GHCup.Version where
|
|||||||
import GHCup.Utils.Version.QQ
|
import GHCup.Utils.Version.QQ
|
||||||
|
|
||||||
import Data.Versions
|
import Data.Versions
|
||||||
|
import URI.ByteString
|
||||||
|
import URI.ByteString.QQ
|
||||||
|
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
-- | This reflects the API version of the JSON.
|
||||||
|
ghcupURL :: URI
|
||||||
|
ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.2.json|]
|
||||||
|
|
||||||
ghcUpVer :: PVP
|
ghcUpVer :: PVP
|
||||||
ghcUpVer = [pver|0.1.0|]
|
ghcUpVer = [pver|0.1.4|]
|
||||||
|
|
||||||
|
numericVer :: String
|
||||||
|
numericVer = T.unpack . prettyPVP $ ghcUpVer
|
||||||
|
|||||||
208
www/LICENSE
Normal file
208
www/LICENSE
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
The ghcup website, excluding ghcup, fonts, bootstrap-haskell script are subject
|
||||||
|
to the license below. Design, javascript and css are used from the rustup
|
||||||
|
project: https://github.com/rust-lang/rustup.rs/tree/master/www
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
1
www/copy.svg
Normal file
1
www/copy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.66em" height="2em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3l3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7c-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2c1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1s-1 .45-1 1s-.45 1-1 1H3c-.55 0-1 .45-1 1z" fill="#626262"/></svg>
|
||||||
|
After Width: | Height: | Size: 696 B |
BIN
www/fonts/FiraSans-Light.woff
Normal file
BIN
www/fonts/FiraSans-Light.woff
Normal file
Binary file not shown.
BIN
www/fonts/FiraSans-Medium.woff
Normal file
BIN
www/fonts/FiraSans-Medium.woff
Normal file
Binary file not shown.
BIN
www/fonts/FiraSans-Regular.woff
Normal file
BIN
www/fonts/FiraSans-Regular.woff
Normal file
Binary file not shown.
BIN
www/fonts/Inconsolata-Regular.ttf
Normal file
BIN
www/fonts/Inconsolata-Regular.ttf
Normal file
Binary file not shown.
92
www/fonts/OFL.txt
Normal file
92
www/fonts/OFL.txt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
Copyright (c) 2011, Raph Levien (firstname.lastname@gmail.com), Copyright (c) 2012, Cyreal (cyreal.org)
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
www/fonts/WorkSans-Medium.ttf
Normal file
BIN
www/fonts/WorkSans-Medium.ttf
Normal file
Binary file not shown.
240
www/ghcup.css
Normal file
240
www/ghcup.css
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: local('Fira Sans Light'), url("fonts/FiraSans-Light.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Fira Sans'), url("fonts/FiraSans-Regular.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: local('Fira Sans Medium'), url("fonts/FiraSans-Medium.woff") format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Work Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: local('Work Sans Medium'), url("fonts/WorkSans-Medium.ttf") format('ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inconsolata';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Inconsolata Regular'), url("fonts/Inconsolata-Regular.ttf") format('ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin-top: 2em;
|
||||||
|
background-color: white;
|
||||||
|
color: #515151;
|
||||||
|
font-family: "Fira Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: Inconsolata,Menlo,Monaco,Consolas,"Courier New",monospace;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
body#idx #pitch > a {
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #428bca;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: rgb(42, 100, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
body#idx > * {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
|
width: 37em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body#idx > #pitch {
|
||||||
|
width: 30rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pitch em {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
body#idx p {
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body#idx p.other-help {
|
||||||
|
font-size: 0.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
background-color: rgb(250, 250, 250);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid rgb(204, 204, 204);
|
||||||
|
box-shadow: 0px 1px 4px 0px rgb(204, 204, 204);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions > * {
|
||||||
|
width: 55rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions div.command-button {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions div.command-button button {
|
||||||
|
color: white;
|
||||||
|
/* border: none; */
|
||||||
|
background-color: rgb(242, 242, 242);
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-top: 25px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions div.command-button button:hover {
|
||||||
|
background: rgb(232, 232, 232);
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions div.command-button button:focus {
|
||||||
|
background: rgb(222, 222, 222);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#platform-instructions-linux > div > pre,
|
||||||
|
#platform-instructions-mac > div > pre,
|
||||||
|
#platform-instructions-freebsd > div > pre,
|
||||||
|
#platform-instructions-win32 > pre,
|
||||||
|
#platform-instructions-win64 > pre,
|
||||||
|
#platform-instructions-default > div > div > pre,
|
||||||
|
#platform-instructions-unknown > div > div > pre {
|
||||||
|
background-color: #515151;
|
||||||
|
color: white;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: inset 0px 0px 20px 0px #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
#platform-instructions-win32 a.windows-download,
|
||||||
|
#platform-instructions-win64 a.windows-download,
|
||||||
|
#platform-instructions-default a.windows-download,
|
||||||
|
#platform-instructions-unknown a.windows-download {
|
||||||
|
display: block;
|
||||||
|
padding-top: 0.4rem;
|
||||||
|
padding-bottom: 0.6rem;
|
||||||
|
font-family: "Work Sans", "Fira Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is the box that prints navigator.platform, navigator.appVersion values */
|
||||||
|
#platform-instructions-unknown > div:first-of-type {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#about {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#about > img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
transform: translateY(11px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#platform-button {
|
||||||
|
background-color: #515151;
|
||||||
|
color: white;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ghcup-command:before {
|
||||||
|
color: #999;
|
||||||
|
content: " $ ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip container */
|
||||||
|
.tooltip {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
/* border-bottom: 1px dotted black; [> If you want dots under the hoverable text <] */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip text */
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 120px;
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
/* Position the tooltip text */
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 125%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -60px;
|
||||||
|
|
||||||
|
/* Fade in tooltip */
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip arrow */
|
||||||
|
.tooltip .tooltiptext::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #555 transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the tooltip text when you mouse over the tooltip container */
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
166
www/ghcup.js
Normal file
166
www/ghcup.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
var platforms = ["default", "unknown", "win32", "win64", "linux", "freebsd", "mac"];
|
||||||
|
var platform_override = null;
|
||||||
|
|
||||||
|
function detect_platform() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
if (platform_override !== null) {
|
||||||
|
return platforms[platform_override];
|
||||||
|
}
|
||||||
|
|
||||||
|
var os = "unknown";
|
||||||
|
|
||||||
|
if (navigator.platform == "Linux x86_64") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux i686") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux i686 on x86_64") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux aarch64") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux armv6l") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux armv7l") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux armv8l") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux ppc64") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux mips") {os = "linux";}
|
||||||
|
if (navigator.platform == "Linux mips64") {os = "linux";}
|
||||||
|
if (navigator.platform == "Mac") {os = "mac";}
|
||||||
|
if (navigator.platform == "Win32") {os = "win32";}
|
||||||
|
if (navigator.platform == "Win64" ||
|
||||||
|
navigator.userAgent.indexOf("WOW64") != -1 ||
|
||||||
|
navigator.userAgent.indexOf("Win64") != -1) { os = "win64"; }
|
||||||
|
if (navigator.platform == "FreeBSD x86_64") {os = "freebsd";}
|
||||||
|
if (navigator.platform == "FreeBSD amd64") {os = "freebsd";}
|
||||||
|
// if (navigator.platform == "NetBSD x86_64") {os = "unix";}
|
||||||
|
// if (navigator.platform == "NetBSD amd64") {os = "unix";}
|
||||||
|
|
||||||
|
// I wish I knew by now, but I don't. Try harder.
|
||||||
|
if (os == "unknown") {
|
||||||
|
if (navigator.appVersion.indexOf("Win")!=-1) {os = "win32";}
|
||||||
|
if (navigator.appVersion.indexOf("Mac")!=-1) {os = "mac";}
|
||||||
|
if (navigator.appVersion.indexOf("FreeBSD")!=-1) {os = "freebsd";}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firefox Quantum likes to hide platform and appVersion but oscpu works
|
||||||
|
if (navigator.oscpu) {
|
||||||
|
if (navigator.oscpu.indexOf("Win32")!=-1) {os = "win32";}
|
||||||
|
if (navigator.oscpu.indexOf("Win64")!=-1) {os = "win64";}
|
||||||
|
if (navigator.oscpu.indexOf("Mac")!=-1) {os = "mac";}
|
||||||
|
if (navigator.oscpu.indexOf("Linux")!=-1) {os = "linux";}
|
||||||
|
if (navigator.oscpu.indexOf("FreeBSD")!=-1) {os = "freebsd";}
|
||||||
|
// if (navigator.oscpu.indexOf("NetBSD")!=-1) {os = "unix";}
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjust_for_platform() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var platform = detect_platform();
|
||||||
|
|
||||||
|
platforms.forEach(function (platform_elem) {
|
||||||
|
var platform_div = document.getElementById("platform-instructions-" + platform_elem);
|
||||||
|
platform_div.style.display = "none";
|
||||||
|
if (platform == platform_elem) {
|
||||||
|
platform_div.style.display = "block";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
adjust_platform_specific_instrs(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjust_platform_specific_instrs(platform) {
|
||||||
|
var platform_specific = document.getElementsByClassName("platform-specific");
|
||||||
|
for (var el of platform_specific) {
|
||||||
|
var el_is_not_win = el.className.indexOf("not-win") !== -1;
|
||||||
|
var el_is_inline = el.tagName.toLowerCase() == "span";
|
||||||
|
var el_visible_style = "block";
|
||||||
|
if (el_is_inline) {
|
||||||
|
el_visible_style = "inline";
|
||||||
|
}
|
||||||
|
if (platform == "win64" || platform == "win32") {
|
||||||
|
if (el_is_not_win) {
|
||||||
|
el.style.display = "none";
|
||||||
|
} else {
|
||||||
|
el.style.display = el_visible_style;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (el_is_not_win) {
|
||||||
|
el.style.display = el_visible_style;
|
||||||
|
} else {
|
||||||
|
el.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cycle_platform() {
|
||||||
|
if (platform_override == null) {
|
||||||
|
platform_override = 0;
|
||||||
|
} else {
|
||||||
|
platform_override = (platform_override + 1) % platforms.length;
|
||||||
|
}
|
||||||
|
adjust_for_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_up_cycle_button() {
|
||||||
|
var cycle_button = document.getElementById("platform-button");
|
||||||
|
cycle_button.onclick = cycle_platform;
|
||||||
|
|
||||||
|
var key="test";
|
||||||
|
var idx=0;
|
||||||
|
var unlocked=false;
|
||||||
|
|
||||||
|
document.onkeypress = function(event) {
|
||||||
|
if (event.key == "n" && unlocked) {
|
||||||
|
cycle_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key == key[idx]) {
|
||||||
|
idx += 1;
|
||||||
|
|
||||||
|
if (idx == key.length) {
|
||||||
|
cycle_button.style.display = "block";
|
||||||
|
unlocked = true;
|
||||||
|
cycle_platform();
|
||||||
|
}
|
||||||
|
} else if (event.key == key[0]) {
|
||||||
|
idx = 1;
|
||||||
|
} else {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function go_to_default_platform() {
|
||||||
|
platform_override = 0;
|
||||||
|
adjust_for_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_up_default_platform_buttons() {
|
||||||
|
var defaults_buttons = document.getElementsByClassName('default-platform-button');
|
||||||
|
for (var i = 0; i < defaults_buttons.length; i++) {
|
||||||
|
defaults_buttons[i].onclick = go_to_default_platform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fill_in_bug_report_values() {
|
||||||
|
var nav_plat = document.getElementById("nav-plat");
|
||||||
|
var nav_app = document.getElementById("nav-app");
|
||||||
|
nav_plat.textContent = navigator.platform;
|
||||||
|
nav_app.textContent = navigator.appVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard() {
|
||||||
|
const text = document.getElementsByClassName("ghcup-command").item(0).innerText;
|
||||||
|
const el = document.createElement('textarea');
|
||||||
|
el.value = text;
|
||||||
|
document.body.appendChild(el);
|
||||||
|
el.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
adjust_for_platform();
|
||||||
|
set_up_cycle_button();
|
||||||
|
set_up_default_platform_buttons();
|
||||||
|
fill_in_bug_report_values();
|
||||||
|
}());
|
||||||
66
www/haskell-logo.svg
Normal file
66
www/haskell-logo.svg
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="109"
|
||||||
|
height="80"
|
||||||
|
viewBox="0 0 109.00001 80"
|
||||||
|
version="1.1"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="haskell-logo.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||||
|
<metadata
|
||||||
|
id="metadata20">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1916"
|
||||||
|
inkscape:window-height="1033"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.0159478"
|
||||||
|
inkscape:cx="298.15447"
|
||||||
|
inkscape:cy="-2.7202801"
|
||||||
|
inkscape:window-x="1366"
|
||||||
|
inkscape:window-y="22"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg14" />
|
||||||
|
<path
|
||||||
|
d="M 1.842,77.722 26.586,40.63 1.842,3.537 H 20.4 L 45.144,40.63 20.4,77.722 Z m 0,0"
|
||||||
|
id="path8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#453a62" />
|
||||||
|
<path
|
||||||
|
d="M 26.586,77.722 51.33,40.63 26.586,3.537 H 45.144 L 94.63,77.722 H 76.074 L 60.61,54.54 45.143,77.722 Z m 0,0"
|
||||||
|
id="path10"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#5e5086" />
|
||||||
|
<path
|
||||||
|
d="M 86.384,56.085 78.136,43.72 h 28.868 V 56.086 H 86.384 Z M 74.012,37.54 65.764,25.175 h 41.24 V 37.54 Z m 0,0"
|
||||||
|
id="path12"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#8f4e8b" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
177
www/index.html
Normal file
177
www/index.html
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>ghcup - The Haskell (GHC) toolchain installer</title>
|
||||||
|
<meta name="keywords" content="Haskell, Haskell programming language, ghc, ghcup">
|
||||||
|
<meta name="description" content="The Haskell (GHC) toolchain installer">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="normalize.css">
|
||||||
|
<link rel="stylesheet" href="ghcup.css">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body id="idx">
|
||||||
|
|
||||||
|
<script id='html-content' type="text/html">
|
||||||
|
<a id="platform-button" style="display: none;" href="#">
|
||||||
|
click or press "n" to cycle platforms
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<p id="pitch">
|
||||||
|
<em>ghcup</em> is an installer for<br/>
|
||||||
|
the general purpose language <a href="https://www.haskell.org/">Haskell</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="platform-instructions-linux" class="instructions" style="display: none;">
|
||||||
|
<p>Run the following in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<div class="command-button"><pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre><button class="tooltip" onclick="copyToClipboard()"><img src="copy.svg" alt="" /><span class="tooltiptext">Copy to clipboard</span></button></div>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.<br/>You appear to be running Linux. If not, <a class="default-platform-button" href="#">display all supported installers</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-mac" class="instructions" style="display: none;">
|
||||||
|
<p>Run the following in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<div class="command-button"><pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre><button class="tooltip" onclick="copyToClipboard()"><img src="copy.svg" alt="" /><span class="tooltiptext">Copy to clipboard</span></button></div>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.<br/>You appear to be running macOS. If not, <a class="default-platform-button" href="#">display all supported installers</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-freebsd" class="instructions" style="display: none;">
|
||||||
|
<p>Run the following in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<div class="command-button"><pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre><button class="tooltip" onclick="copyToClipboard()"><img src="copy.svg" alt="" /><span class="tooltiptext">Copy to clipboard</span></button></div>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.<br/>You appear to be running FreeBSD. If not, <a class="default-platform-button" href="#">display all supported installers</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-win32" class="instructions">
|
||||||
|
<p>
|
||||||
|
To install Haskell, follow the instructions on
|
||||||
|
<a class="windows-download" href="https://www.haskell.org/platform/#windows">Haskell Platform</a>
|
||||||
|
</p>
|
||||||
|
<p class="other-help">You appear to be running Windows 32-bit. If not, <a class="default-platform-button" href="#">display all supported installers</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-win64" class="instructions" style="display: none;">
|
||||||
|
<p>
|
||||||
|
To install Haskell, follow the instructions on
|
||||||
|
<a class="windows-download" href="https://www.haskell.org/platform/#windows">Haskell Platform</a>
|
||||||
|
</p>
|
||||||
|
<p class="other-help">You appear to be running Windows 64-bit. If not, <a class="default-platform-button" href="#">display all supported installers</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-unknown" class="instructions" style="display: none;">
|
||||||
|
<!-- unrecognized platform: ask for help -->
|
||||||
|
<p>I don't recognize your platform.</p>
|
||||||
|
<p>
|
||||||
|
ghcup runs on Linux, macOS and FreeBSD. If
|
||||||
|
you are on one of these platforms and are seeing this then please
|
||||||
|
<a href="https://gitlab.haskell.org/haskell/ghcup-hs/issues">report an issue</a>,
|
||||||
|
along with the following values:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>navigator.platform:</div>
|
||||||
|
<div id="nav-plat"></div>
|
||||||
|
<div>navigator.appVersion:</div>
|
||||||
|
<div id="nav-app"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- duplicate the default cross-platform instructions -->
|
||||||
|
<div>
|
||||||
|
<p>If you are running Linux, macOS or FreeBSD,<br/>run the following in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<div class="command-button"><pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre><button class="tooltip" onclick="copyToClipboard()"><img src="copy.svg" alt="" /><span class="tooltiptext">Copy to clipboard</span></button></div>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
If you are running Windows,<br/>follow the instructions on
|
||||||
|
<a class="windows-download" href="https://www.haskell.org/platform/#windows">Haskell Platform</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="platform-instructions-default" class="instructions">
|
||||||
|
<div>
|
||||||
|
<p>To install Haskell, if you are running Linux, macOS or FreeBSD,<br/>run the following
|
||||||
|
in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<div class="command-button"><pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre><button class="tooltip" onclick="copyToClipboard()"><img src="copy.svg" alt="" /><span class="tooltiptext">Copy to clipboard</span></button></div>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
If you are running Windows,<br/>follow the instructions on
|
||||||
|
<a class="windows-download" href="https://www.haskell.org/platform/#windows">Haskell Platform</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Need help? <a href="http://webchat.freenode.net/?randomnick=1&channels=%23haskell&uio=d4">Ask on #haskell</a> or <a href="https://gitlab.haskell.org/haskell/ghcup-hs/issues">report a bug</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p id="about">
|
||||||
|
<img src="haskell-logo.svg" alt="" />
|
||||||
|
ghcup is a haskell.org hosted project.
|
||||||
|
<br/>
|
||||||
|
<a href="https://www.haskell.org/downloads/">other installation options</a>
|
||||||
|
·
|
||||||
|
<a href="https://gitlab.haskell.org/haskell/ghcup-hs">about ghcup</a>
|
||||||
|
·
|
||||||
|
<a href="https://github.com/rust-lang/rustup.rs/tree/master/www">web design from rustup</a>
|
||||||
|
</p>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
document.write(document.getElementById("html-content").innerHTML);
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="ghcup.js"></script>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<p id="pitch">
|
||||||
|
<em>ghcup</em> is an installer for<br/>
|
||||||
|
the general purpose language <a href="https://www.haskell.org/">Haskell</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div id="platform-instructions-default" class="instructions">
|
||||||
|
<div>
|
||||||
|
<p>To install Haskell, if you are running Linux, macOS or FreeBSD,<br/>run the following
|
||||||
|
in your terminal (as a user other than root), then follow the onscreen instructions.</p>
|
||||||
|
<pre><span class='ghcup-command'>curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh</span></pre>
|
||||||
|
<p class="other-help">If you don't like curl | sh, see <a href="https://gitlab.haskell.org/haskell/ghcup-hs#manual-install">other installation methods</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
If you are running Windows,<br/>follow the instructions on
|
||||||
|
<a class="windows-download" href="https://www.haskell.org/platform/#windows">Haskell Platform</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Need help? <a href="http://webchat.freenode.net/?randomnick=1&channels=%23haskell&uio=d4">Ask on #haskell</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p id="about">
|
||||||
|
<img src="haskell-logo.svg" alt="" />
|
||||||
|
ghcup is a haskell.org hosted project.
|
||||||
|
<br/>
|
||||||
|
<a href="https://www.haskell.org/downloads/">other installation options</a>
|
||||||
|
·
|
||||||
|
<a href="https://gitlab.haskell.org/haskell/ghcup-hs">about ghcup</a>
|
||||||
|
·
|
||||||
|
<a href="https://github.com/rust-lang/rustup.rs/tree/master/www">web design from rustup</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
427
www/normalize.css
vendored
Normal file
427
www/normalize.css
vendored
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Set default font family to sans-serif.
|
||||||
|
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||||
|
* user zoom.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 2 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove default margin.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML5 display definitions
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct `block` display not defined for any HTML5 element in IE 8/9.
|
||||||
|
* Correct `block` display not defined for `details` or `summary` in IE 10/11
|
||||||
|
* and Firefox.
|
||||||
|
* Correct `block` display not defined for `main` in IE 11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
details,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
main,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section,
|
||||||
|
summary {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct `inline-block` display not defined in IE 8/9.
|
||||||
|
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
progress,
|
||||||
|
video {
|
||||||
|
display: inline-block; /* 1 */
|
||||||
|
vertical-align: baseline; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent modern browsers from displaying `audio` without controls.
|
||||||
|
* Remove excess height in iOS 5 devices.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address `[hidden]` styling not present in IE 8/9/10.
|
||||||
|
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden],
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background color from active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improve readability when focused and also mouse hovered in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: 1px dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in Safari and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address variable `h1` font-size and margin within `section` and `article`
|
||||||
|
* contexts in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in IE 8/9.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address inconsistent and variable font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove border when inside `a` element in IE 8/9/10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct overflow not hidden in IE 9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address margin not present in IE 8/9 and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address differences between Firefox and other browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contain overflow in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address odd `em`-unit font size rendering in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known limitation: by default, Chrome and Safari on OS X allow very limited
|
||||||
|
* styling of `select`, unless a `border` property is set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct color not being inherited.
|
||||||
|
* Known issue: affects color of disabled elements.
|
||||||
|
* 2. Correct font properties not being inherited.
|
||||||
|
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
color: inherit; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
margin: 0; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address `overflow` set to `hidden` in IE 8/9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||||
|
* All other form control elements do not inherit `text-transform` values.
|
||||||
|
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
|
||||||
|
* Correct `select` style inheritance in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||||
|
* and `video` controls.
|
||||||
|
* 2. Correct inability to style clickable `input` types in iOS.
|
||||||
|
* 3. Improve usability and consistency of cursor style between image-type
|
||||||
|
* `input` and others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html input[type="button"], /* 1 */
|
||||||
|
input[type="reset"],
|
||||||
|
input[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
cursor: pointer; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-set default cursor for disabled elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button[disabled],
|
||||||
|
html input[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inner padding and border in Firefox 4+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||||
|
* the UA stylesheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It's recommended that you don't attempt to style these elements.
|
||||||
|
* Firefox's implementation doesn't respect box-sizing, padding, or width.
|
||||||
|
*
|
||||||
|
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
||||||
|
* 2. Remove excess padding in IE 8/9/10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
||||||
|
* `font-size` values of the `input`, it causes the cursor style of the
|
||||||
|
* decrement button to change from `default` to `text`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="number"]::-webkit-inner-spin-button,
|
||||||
|
input[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
||||||
|
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
||||||
|
* (include `-moz` to future-proof).
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
-webkit-box-sizing: content-box; /* 2 */
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
||||||
|
* Safari (but not Chrome) clips the cancel button when the search input has
|
||||||
|
* padding (and `textfield` appearance).
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
|
input[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define consistent border, margin, and padding.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 0.35em 0.625em 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct `color` not being inherited in IE 8/9/10/11.
|
||||||
|
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border: 0; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove default vertical scrollbar in IE 8/9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't inherit the `font-weight` (applied by a rule above).
|
||||||
|
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
|
||||||
|
*/
|
||||||
|
|
||||||
|
optgroup {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove most spacing between table cells.
|
||||||
|
*/
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user