Compare commits

..

2 Commits

Author SHA1 Message Date
e88e131b9d Update .gitignore 2021-10-17 16:13:27 +02:00
4c7c9ab62e Refactor app Main 2021-10-17 16:11:36 +02:00
105 changed files with 17740 additions and 6738 deletions

View File

@@ -44,8 +44,8 @@ jobs:
- uses: haskell/actions/setup@v1.2 - uses: haskell/actions/setup@v1.2
with: with:
ghc-version: 8.10.7 ghc-version: 8.10.4
cabal-version: 3.6.2.0 cabal-version: 3.4.0.0
- name: create ~/.local/bin - name: create ~/.local/bin
run: mkdir -p "$HOME/.local/bin" run: mkdir -p "$HOME/.local/bin"

View File

@@ -11,13 +11,6 @@ variables:
# Commit of ghc/ci-images repository from which to pull Docker images # Commit of ghc/ci-images repository from which to pull Docker images
DOCKER_REV: 8d0224e6b2a08157649651e69302380b2bd24e11 DOCKER_REV: 8d0224e6b2a08157649651e69302380b2bd24e11
# Sequential version number of all cached things.
# Bump to invalidate GitLab CI cache.
CACHE_REV: 1
GIT_SUBMODULE_STRATEGY: recursive
############################################################ ############################################################
# CI Step # CI Step
############################################################ ############################################################
@@ -118,17 +111,13 @@ variables:
script: script:
- bash ./.gitlab/script/ghcup_version.sh - bash ./.gitlab/script/ghcup_version.sh
variables: variables:
JSON_VERSION: "0.0.7" JSON_VERSION: "0.0.6"
artifacts: artifacts:
expire_in: 2 week expire_in: 2 week
paths: paths:
- test/golden - test/golden
- dist-newstyle/cache/ - dist-newstyle/cache/
when: on_failure when: on_failure
cache:
key: ghcup-test-$CACHE_REV
paths:
- cabal-cache
# .test_ghcup_scoop: # .test_ghcup_scoop:
# script: # script:
@@ -140,10 +129,6 @@ variables:
- .debian - .debian
before_script: before_script:
- ./.gitlab/before_script/linux/install_deps.sh - ./.gitlab/before_script/linux/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:linux32: .test_ghcup_version:linux32:
extends: extends:
@@ -151,10 +136,6 @@ variables:
- .alpine:32bit - .alpine:32bit
before_script: before_script:
- ./.gitlab/before_script/linux/alpine/install_deps.sh - ./.gitlab/before_script/linux/alpine/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:armv7: .test_ghcup_version:armv7:
extends: extends:
@@ -162,10 +143,6 @@ variables:
- .linux:armv7 - .linux:armv7
before_script: before_script:
- ./.gitlab/before_script/linux/install_deps.sh - ./.gitlab/before_script/linux/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:aarch64: .test_ghcup_version:aarch64:
extends: extends:
@@ -173,96 +150,71 @@ variables:
- .linux:aarch64 - .linux:aarch64
before_script: before_script:
- ./.gitlab/before_script/linux/install_deps.sh - ./.gitlab/before_script/linux/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:darwin: .test_ghcup_version:darwin:
extends: extends:
- .test_ghcup_version - .test_ghcup_version
- .darwin - .darwin
- .root_cleanup
before_script: before_script:
- ./.gitlab/before_script/darwin/install_deps.sh - ./.gitlab/before_script/darwin/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:darwin:aarch64: .test_ghcup_version:darwin:aarch64:
extends: extends:
- .test_ghcup_version - .test_ghcup_version
- .darwin:aarch64 - .darwin:aarch64
cache: - .root_cleanup
key: darwin-brew-$CACHE_REV
paths:
- brew_cache
key: ghcup-test-$CACHE_REV
paths:
- cabal-cache
before_script:
# extract brew cache
- ./.gitlab/script/ci.sh extract_brew_cache
# otherwise we seem to get intel binaries
- export HOMEBREW_CHANGE_ARCH_TO_ARM=1
# update and install packages
- /bin/bash ./.gitlab/script/brew.sh llvm autoconf automake
# extract cabal cache
- ./.gitlab/script/ci.sh extract_cabal_cache
script: | script: |
export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH" set -Eeuo pipefail
export CC=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang function runInNixShell() {
export CXX=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang++ time nix-shell $CI_PROJECT_DIR/.gitlab/shell.nix \
export LD=ld -I nixpkgs=https://github.com/angerman/nixpkgs/archive/75f7281738b.tar.gz \
export AR=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ar --argstr system "aarch64-darwin" \
export RANLIB=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ranlib --pure \
./.gitlab/before_script/darwin/install_deps.sh --keep CI_PROJECT_DIR \
./.gitlab/script/ghcup_version.sh --keep MACOSX_DEPLOYMENT_TARGET \
after_script: --keep JSON_VERSION \
- ./.gitlab/script/ci.sh save_cabal_cache --keep ARTIFACT \
- ./.gitlab/script/ci.sh save_brew_cache --keep OS \
- bash ./.gitlab/after_script.sh --keep ARCH \
--keep CABAL_DIR \
--keep GHC_VERSION \
--keep CABAL_VERSION \
--run "$1" 2>&1
}
runInNixShell ./.gitlab/before_script/darwin/install_deps.sh 2>&1
runInNixShell ./.gitlab/script/ghcup_version.sh 2>&1
.test_ghcup_version:freebsd12: .test_ghcup_version:freebsd12:
extends: extends:
- .test_ghcup_version - .test_ghcup_version
- .freebsd12 - .freebsd12
- .root_cleanup
before_script: before_script:
- ./.gitlab/before_script/freebsd/install_deps.sh - ./.gitlab/before_script/freebsd/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:freebsd13: .test_ghcup_version:freebsd13:
extends: extends:
- .test_ghcup_version - .test_ghcup_version
- .freebsd13 - .freebsd13
- .root_cleanup
before_script: before_script:
- sudo pkg update
- sudo pkg install --yes compat12x-amd64
- sudo ln -s libncurses.so.6 /usr/local/lib/libncurses.so.6.2
- ./.gitlab/before_script/freebsd/install_deps.sh - ./.gitlab/before_script/freebsd/install_deps.sh
- ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
.test_ghcup_version:windows: .test_ghcup_version:windows:
extends: extends:
- .test_ghcup_version - .test_ghcup_version
- .windows - .windows
- .root_cleanup
before_script: before_script:
- set CABAL_DIR="$CI_PROJECT_DIR/cabal"
- bash ./.gitlab/before_script/windows/install_deps.sh - bash ./.gitlab/before_script/windows/install_deps.sh
- bash ./.gitlab/script/ci.sh extract_cabal_cache
after_script:
- bash ./.gitlab/script/ci.sh save_cabal_cache
- bash ./.gitlab/after_script.sh
# .test_ghcup_scoop:windows: # .test_ghcup_scoop:windows:
# extends: # extends:
# - .windows # - .windows
# - .test_ghcup_scoop # - .test_ghcup_scoop
# - .root_cleanup
.release_ghcup: .release_ghcup:
script: script:
@@ -275,19 +227,16 @@ variables:
only: only:
- tags - tags
variables: variables:
JSON_VERSION: "0.0.7" JSON_VERSION: "0.0.6"
######## stack test ######## ######## stack test ########
test:linux:stack: test:linux:stack:
stage: test stage: test
before_script: before_script:
- ./.gitlab/script/ci.sh extract_stack_cache
- ./.gitlab/before_script/linux/install_deps_minimal.sh - ./.gitlab/before_script/linux/install_deps_minimal.sh
script: script:
- ./.gitlab/script/ghcup_stack.sh - ./.gitlab/script/ghcup_stack.sh
after_script:
- ./.gitlab/script/ci.sh save_stack_cache
extends: extends:
- .debian - .debian
needs: [] needs: []
@@ -317,7 +266,6 @@ test:windows:bootstrap_powershell_script:
- "[Environment]::SetEnvironmentVariable('GHCUP_MSYS2', $null, [System.EnvironmentVariableTarget]::User)" - "[Environment]::SetEnvironmentVariable('GHCUP_MSYS2', $null, [System.EnvironmentVariableTarget]::User)"
- "[Environment]::SetEnvironmentVariable('CABAL_DIR', $null, [System.EnvironmentVariableTarget]::User)" - "[Environment]::SetEnvironmentVariable('CABAL_DIR', $null, [System.EnvironmentVariableTarget]::User)"
- bash ./.gitlab/after_script.sh - bash ./.gitlab/after_script.sh
- bash ./.gitlab/script/ci.sh save_cabal_cache
variables: variables:
GHC_VERSION: "8.10.7" GHC_VERSION: "8.10.7"
CABAL_VERSION: "3.6.2.0" CABAL_VERSION: "3.6.2.0"
@@ -440,7 +388,6 @@ test:mac:aarch64:
CABAL_VERSION: "3.6.2.0" CABAL_VERSION: "3.6.2.0"
needs: [] needs: []
allow_failure: true allow_failure: true
when: manual
######## freebsd test ######## ######## freebsd test ########
@@ -561,39 +508,32 @@ release:darwin:aarch64:
- .darwin:aarch64 - .darwin:aarch64
- .release_ghcup - .release_ghcup
- .root_cleanup - .root_cleanup
cache:
key: darwin-brew-$CACHE_REV
paths:
- brew_cache
key: ghcup-test-$CACHE_REV
paths:
- cabal-cache
before_script:
- ./.gitlab/script/ci.sh extract_brew_cache
- ./.gitlab/script/ci.sh extract_cabal_cache
# otherwise we seem to get intel binaries
- export HOMEBREW_CHANGE_ARCH_TO_ARM=1
# update and install packages
- /bin/bash ./.gitlab/script/brew.sh llvm autoconf automake
script: | script: |
export PATH="$CI_PROJECT_DIR/.brew/opt/llvm/bin:$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH" set -Eeuo pipefail
export CC=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang function runInNixShell() {
export CXX=$CI_PROJECT_DIR/.brew/opt/llvm/bin/clang++ time nix-shell $CI_PROJECT_DIR/.gitlab/shell.nix \
export LD=ld -I nixpkgs=https://github.com/angerman/nixpkgs/archive/75f7281738b.tar.gz \
export AR=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ar --argstr system "aarch64-darwin" \
export RANLIB=$CI_PROJECT_DIR/.brew/opt/llvm/bin/llvm-ranlib --pure \
./.gitlab/before_script/darwin/install_deps.sh --keep CI_PROJECT_DIR \
./.gitlab/script/ghcup_release.sh --keep MACOSX_DEPLOYMENT_TARGET \
after_script: --keep JSON_VERSION \
- ./.gitlab/script/ci.sh save_cabal_cache --keep ARTIFACT \
- ./.gitlab/script/ci.sh save_brew_cache --keep OS \
--keep ARCH \
--keep CABAL_DIR \
--keep GHC_VERSION \
--keep CABAL_VERSION \
--run "$1" 2>&1
}
runInNixShell ./.gitlab/before_script/darwin/install_deps.sh 2>&1
runInNixShell ./.gitlab/script/ghcup_release.sh 2>&1
variables: variables:
ARTIFACT: "aarch64-apple-darwin-ghcup" ARTIFACT: "aarch64-apple-darwin-ghcup"
GHC_VERSION: "8.10.7" GHC_VERSION: "8.10.7"
CABAL_VERSION: "3.6.2.0" CABAL_VERSION: "3.6.2.0"
MACOSX_DEPLOYMENT_TARGET: "10.7" MACOSX_DEPLOYMENT_TARGET: "10.7"
allow_failure: true allow_failure: true
when: manual
######## freebsd release ######## ######## freebsd release ########
@@ -621,9 +561,6 @@ release:freebsd13:
- .release_ghcup - .release_ghcup
- .root_cleanup - .root_cleanup
before_script: before_script:
- sudo pkg update
- sudo pkg install --yes compat12x-amd64
- sudo ln -s libncurses.so.6 /usr/local/lib/libncurses.so.6.2
- ./.gitlab/before_script/freebsd/install_deps.sh - ./.gitlab/before_script/freebsd/install_deps.sh
variables: variables:
ARTIFACT: "x86_64-portbld-freebsd-ghcup" ARTIFACT: "x86_64-portbld-freebsd-ghcup"

View File

@@ -12,8 +12,4 @@ if [ "${OS}" = "WINDOWS" ] ; then
rm -Rf /c/ghcup rm -Rf /c/ghcup
fi fi
if [ "${OS}" = "DARWIN" ] ; then
rm -Rf /private/tmp/.brew_tmp
fi
exit 0 exit 0

View File

@@ -8,15 +8,7 @@ set -eux
mkdir -p "${TMPDIR}" mkdir -p "${TMPDIR}"
if freebsd-version | grep -E '^12.*' ; then curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-portbld-freebsd-ghcup > ./ghcup-bin
freebsd_ver=12
elif freebsd-version | grep -E '^13.*' ; then
freebsd_ver=13
else
(>&2 echo "Unsupported FreeBSD version! Please report a bug at https://gitlab.haskell.org/haskell/ghcup-hs/-/issues")
exit 1
fi
curl -sSfL https://downloads.haskell.org/~ghcup/x86_64-freebsd${freebsd_ver}-ghcup > ./ghcup-bin
chmod +x ghcup-bin chmod +x ghcup-bin
./ghcup-bin -v upgrade -i -f ./ghcup-bin -v upgrade -i -f

View File

@@ -1,37 +0,0 @@
.
./cabal
./ghc
./ghc-8.10.7
./ghc-pkg
./ghc-pkg-8.10.7
./ghci
./ghci-8.10.7
./haddock
./haddock-8.10.7
./haskell-language-server-8.10.6
./haskell-language-server-8.10.6~1.6.1.0
./haskell-language-server-8.10.7
./haskell-language-server-8.10.7~1.6.1.0
./haskell-language-server-8.6.5
./haskell-language-server-8.6.5~1.6.1.0
./haskell-language-server-8.8.4
./haskell-language-server-8.8.4~1.6.1.0
./haskell-language-server-9.0.1
./haskell-language-server-9.0.1~1.6.1.0
./haskell-language-server-9.0.2
./haskell-language-server-9.0.2~1.6.1.0
./haskell-language-server-9.2.1
./haskell-language-server-9.2.1~1.6.1.0
./haskell-language-server-wrapper
./haskell-language-server-wrapper-1.6.1.0
./hp2ps
./hp2ps-8.10.7
./hpc
./hpc-8.10.7
./hsc2hs
./hsc2hs-8.10.7
./runghc
./runghc-8.10.7
./runhaskell
./runhaskell-8.10.7
./stack

View File

@@ -1,81 +0,0 @@
.
./cabal.exe
./cabal.shim
./ghc-8.10.7.exe
./ghc-8.10.7.shim
./ghc-pkg-8.10.7.exe
./ghc-pkg-8.10.7.shim
./ghc-pkg.exe
./ghc-pkg.shim
./ghc.exe
./ghc.shim
./ghci-8.10.7.exe
./ghci-8.10.7.shim
./ghci.exe
./ghci.shim
./ghcii-8.10.7.sh-8.10.7.exe
./ghcii-8.10.7.sh-8.10.7.shim
./ghcii-8.10.7.sh.exe
./ghcii-8.10.7.sh.shim
./ghcii.sh-8.10.7.exe
./ghcii.sh-8.10.7.shim
./ghcii.sh.exe
./ghcii.sh.shim
./haddock-8.10.7.exe
./haddock-8.10.7.shim
./haddock.exe
./haddock.shim
./haskell-language-server-8.10.6.exe
./haskell-language-server-8.10.6.shim
./haskell-language-server-8.10.6~1.6.1.0.exe
./haskell-language-server-8.10.6~1.6.1.0.shim
./haskell-language-server-8.10.7.exe
./haskell-language-server-8.10.7.shim
./haskell-language-server-8.10.7~1.6.1.0.exe
./haskell-language-server-8.10.7~1.6.1.0.shim
./haskell-language-server-8.6.5.exe
./haskell-language-server-8.6.5.shim
./haskell-language-server-8.6.5~1.6.1.0.exe
./haskell-language-server-8.6.5~1.6.1.0.shim
./haskell-language-server-8.8.4.exe
./haskell-language-server-8.8.4.shim
./haskell-language-server-8.8.4~1.6.1.0.exe
./haskell-language-server-8.8.4~1.6.1.0.shim
./haskell-language-server-9.0.1.exe
./haskell-language-server-9.0.1.shim
./haskell-language-server-9.0.1~1.6.1.0.exe
./haskell-language-server-9.0.1~1.6.1.0.shim
./haskell-language-server-9.0.2.exe
./haskell-language-server-9.0.2.shim
./haskell-language-server-9.0.2~1.6.1.0.exe
./haskell-language-server-9.0.2~1.6.1.0.shim
./haskell-language-server-9.2.1.exe
./haskell-language-server-9.2.1.shim
./haskell-language-server-9.2.1~1.6.1.0.exe
./haskell-language-server-9.2.1~1.6.1.0.shim
./haskell-language-server-wrapper-1.6.1.0.exe
./haskell-language-server-wrapper-1.6.1.0.shim
./haskell-language-server-wrapper.exe
./haskell-language-server-wrapper.shim
./hp2ps-8.10.7.exe
./hp2ps-8.10.7.shim
./hp2ps.exe
./hp2ps.shim
./hpc-8.10.7.exe
./hpc-8.10.7.shim
./hpc.exe
./hpc.shim
./hsc2hs-8.10.7.exe
./hsc2hs-8.10.7.shim
./hsc2hs.exe
./hsc2hs.shim
./runghc-8.10.7.exe
./runghc-8.10.7.shim
./runghc.exe
./runghc.shim
./runhaskell-8.10.7.exe
./runhaskell-8.10.7.shim
./runhaskell.exe
./runhaskell.shim
./stack.exe
./stack.shim

View File

@@ -1,23 +1,11 @@
if [ "${OS}" = "WINDOWS" ] ; then if [ "${OS}" = "WINDOWS" ] ; then
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR" export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
export GHCUP_BIN="$CI_PROJECT_DIR/ghcup/bin" export GHCUP_BIN="$CI_PROJECT_DIR/ghcup/bin"
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH" export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH"
export TMPDIR="$CI_PROJECT_DIR/tmp" export TMPDIR="$CI_PROJECT_DIR/tmp"
export CABAL_DIR="$CI_PROJECT_DIR/cabal"
export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
export STACK_ROOT="$CI_PROJECT_DIR/stack"
export STACK_CACHE="$CI_PROJECT_DIR/stack-cache"
export BREW_DIR="$CI_PROJECT_DIR/.brew_cache"
export BREW_CACHE="$CI_PROJECT_DIR/brew-cache"
else else
export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR" export GHCUP_INSTALL_BASE_PREFIX="$CI_PROJECT_DIR"
export GHCUP_BIN="$CI_PROJECT_DIR/.ghcup/bin" export GHCUP_BIN="$CI_PROJECT_DIR/.ghcup/bin"
export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:$PATH" export PATH="$GHCUP_BIN:$CI_PROJECT_DIR/.local/bin:/opt/llvm/bin:$PATH"
export TMPDIR="$CI_PROJECT_DIR/tmp" export TMPDIR="$CI_PROJECT_DIR/tmp"
export CABAL_DIR="$CI_PROJECT_DIR/cabal"
export CABAL_CACHE="$CI_PROJECT_DIR/cabal-cache"
export STACK_ROOT="$CI_PROJECT_DIR/stack"
export STACK_CACHE="$CI_PROJECT_DIR/stack-cache"
export BREW_DIR="$CI_PROJECT_DIR/.brew_cache"
export BREW_CACHE="$CI_PROJECT_DIR/brew-cache"
fi fi

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
set -Eeuxo pipefail
# Install brew locally in the project dir. Packages will also be installed here.
[ -e "$CI_PROJECT_DIR/.brew" ] || git clone --depth=1 https://github.com/Homebrew/brew $CI_PROJECT_DIR/.brew
export PATH="$CI_PROJECT_DIR/.brew/bin:$CI_PROJECT_DIR/.brew/sbin:$PATH"
# make sure to not pollute the machine with temp files etc
mkdir -p $CI_PROJECT_DIR/.brew_cache
export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache
mkdir -p $CI_PROJECT_DIR/.brew_logs
export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs
mkdir -p /private/tmp/.brew_tmp
export HOMEBREW_TEMP=/private/tmp/.brew_tmp
# update and install packages
brew update
brew install ${1+"$@"}

View File

@@ -1,70 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
TOP="$( cd "$(dirname "$0")" ; pwd -P )"
. "${TOP}/../ghcup_env"
function save_cabal_cache () {
echo "Storing cabal cache from $CABAL_DIR to $CABAL_CACHE..."
rm -Rf "$CABAL_CACHE"
mkdir -p "$CABAL_CACHE"
if [ -d "$CABAL_DIR" ]; then
cp -Rf "$CABAL_DIR" "$CABAL_CACHE/"
fi
}
function extract_cabal_cache () {
if [ -d "$CABAL_CACHE" ]; then
echo "Extracting cabal cache from $CABAL_CACHE to $CABAL_DIR..."
mkdir -p "$CABAL_DIR"
cp -Rf "$CABAL_CACHE"/* "$CABAL_DIR"
fi
}
function save_stack_cache () {
echo "Storing stack cache from $STACK_ROOT to $STACK_CACHE..."
rm -Rf "$STACK_CACHE"
mkdir -p "$STACK_CACHE"
if [ -d "$STACK_ROOT" ]; then
cp -Rf "$STACK_DIR" "$STACK_CACHE"
fi
}
function extract_stack_cache () {
if [ -d "$STACK_CACHE" ]; then
echo "Extracting stack cache from $STACK_CACHE to $STACK_ROOT..."
mkdir -p "$STACK_ROOT"
cp -Rf "$STACK_CACHE"/* "$STACK_ROOT"
fi
}
function save_brew_cache () {
echo "Storing brew cache from $BREW_DIR to $BREW_CACHE..."
rm -Rf "$BREW_CACHE"
mkdir -p "$BREW_CACHE"
if [ -d "$BREW_DIR" ]; then
cp -Rf "$BREW_DIR" "$BREW_CACHE"
fi
}
function extract_brew_cache () {
if [ -d "$BREW_CACHE" ]; then
echo "Extracting stack cache from $BREW_CACHE to $BREW_DIR..."
mkdir -p "$BREW_DIR"
cp -Rf "$BREW_CACHE"/* "$BREW_DIR"
fi
}
case $1 in
extract_cabal_cache) extract_cabal_cache ;;
save_cabal_cache) save_cabal_cache ;;
extract_stack_cache) extract_stack_cache ;;
save_stack_cache) save_stack_cache ;;
extract_brew_cache) extract_brew_cache ;;
save_brew_cache) save_brew_cache ;;
*) echo "unknown mode $1" ; exit 11 ;;
esac

View File

@@ -41,7 +41,7 @@ cabal --version
eghcup debug-info eghcup debug-info
eghcup compile hls -j $(nproc) -v ${HLS_TARGET_VERSION} --ghc ${GHC_VERSION} eghcup compile hls -j $(nproc) -v ${HLS_TARGET_VERSION} ${GHC_VERSION}
[ `$(eghcup whereis hls ${HLS_TARGET_VERSION}) --numeric-version` = "${HLS_TARGET_VERSION}" ] || [ `$(eghcup whereis hls ${HLS_TARGET_VERSION}) --numeric-version | sed 's/.0$//'` = "${HLS_TARGET_VERSION}" ] [ `$(eghcup whereis hls ${HLS_TARGET_VERSION}) --numeric-version` = "${HLS_TARGET_VERSION}" ] || [ `$(eghcup whereis hls ${HLS_TARGET_VERSION}) --numeric-version | sed 's/.0$//'` = "${HLS_TARGET_VERSION}" ]

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/sh
set -eux set -eux
@@ -8,7 +8,6 @@ mkdir -p "$CI_PROJECT_DIR"/.local/bin
CI_PROJECT_DIR=$(pwd) CI_PROJECT_DIR=$(pwd)
ecabal() { ecabal() {
cabal "$@" cabal "$@"
} }
@@ -35,8 +34,6 @@ git describe --always
### build ### build
rm -rf "${GHCUP_DIR}"/share
ecabal update ecabal update
if [ "${OS}" = "DARWIN" ] ; then if [ "${OS}" = "DARWIN" ] ; then
@@ -86,6 +83,7 @@ else
ext='' ext=''
fi fi
cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup')" "$CI_PROJECT_DIR"/.local/bin/ghcup${ext} cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup')" "$CI_PROJECT_DIR"/.local/bin/ghcup${ext}
cp "$(ecabal new-exec -w ghc-${GHC_VERSION} --verbose=0 --offline sh -- -c 'command -v ghcup-gen')" "$CI_PROJECT_DIR"/.local/bin/ghcup-gen${ext}
### cleanup ### cleanup
@@ -94,35 +92,19 @@ rm -rf "${GHCUP_DIR}"
### manual cli based testing ### manual cli based testing
ghcup-gen check -f data/metadata/ghcup-${JSON_VERSION}.yaml
eghcup --numeric-version eghcup --numeric-version
eghcup install ghc ${GHC_VERSION} eghcup install ghc ${GHC_VERSION}
ls -lah "$(eghcup whereis -d ghc ${GHC_VERSION})" [ `$(eghcup whereis ghc ${GHC_VERSION}) --numeric-version` = "${GHC_VERSION}" ]
[ "`$(eghcup whereis ghc ${GHC_VERSION}) --numeric-version`" = "${GHC_VERSION}" ]
[ "`eghcup run --ghc ${GHC_VERSION} -- ghc --numeric-version`" = "${GHC_VERSION}" ]
eghcup set ghc ${GHC_VERSION} eghcup set ghc ${GHC_VERSION}
eghcup install cabal ${CABAL_VERSION} eghcup install cabal ${CABAL_VERSION}
[ "`$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version`" = "${CABAL_VERSION}" ] [ `$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version` = "${CABAL_VERSION}" ]
eghcup unset cabal eghcup unset cabal
"$GHCUP_BIN"/cabal --version && exit 1 || echo yes "$GHCUP_BIN"/cabal --version && exit || echo yes
eghcup set cabal ${CABAL_VERSION} eghcup set cabal ${CABAL_VERSION}
[ "`$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version`" = "${CABAL_VERSION}" ] [ `$(eghcup whereis cabal ${CABAL_VERSION}) --numeric-version` = "${CABAL_VERSION}" ]
[ "`eghcup run --cabal ${CABAL_VERSION} -- cabal --numeric-version`" = "${CABAL_VERSION}" ]
if [ "${OS}" != "FREEBSD" ] ; then
if [ "${ARCH}" = "64" ] ; then
eghcup run --ghc 8.10.7 --cabal 3.4.1.0 --hls 1.6.1.0 --stack 2.7.3 --install --bindir "$(pwd)/.bin"
if [ "${OS}" == "WINDOWS" ] ; then
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup-run.files.windows" | sort)
else
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup-run.files" | sort)
fi
actual=$(cd ".bin" && find . | sort)
[ "${actual}" = "${expected}" ]
unset actual expected
rm -rf .bin
fi
fi
cabal --version cabal --version
@@ -152,7 +134,7 @@ else
eghcup --offline install ghc 8.10.3 eghcup --offline install ghc 8.10.3
if [ "${ARCH}" = "64" ] ; then if [ "${ARCH}" = "64" ] ; then
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.files" | sort) expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-linux.files" | sort)
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find . | sort) actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
[ "${actual}" = "${expected}" ] [ "${actual}" = "${expected}" ]
unset actual expected unset actual expected
fi fi
@@ -160,7 +142,7 @@ else
eghcup prefetch ghc 8.10.3 eghcup prefetch ghc 8.10.3
eghcup --offline install ghc 8.10.3 eghcup --offline install ghc 8.10.3
expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-windows.files" | sort) expected=$(cat "$( cd "$(dirname "$0")" ; pwd -P )/../ghc-8.10.3-windows.files" | sort)
actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find . | sort) actual=$(cd "${GHCUP_DIR}/ghc/8.10.3/" && find | sort)
[ "${actual}" = "${expected}" ] [ "${actual}" = "${expected}" ]
unset actual expected unset actual expected
else else
@@ -174,14 +156,12 @@ else
eghcup set ${GHC_VERSION} eghcup set ${GHC_VERSION}
[ "$(ghc --numeric-version)" = "${ghc_ver}" ] [ "$(ghc --numeric-version)" = "${ghc_ver}" ]
eghcup unset ghc eghcup unset ghc
"$GHCUP_BIN"/ghc --numeric-version && exit 1 || echo yes "$GHCUP_BIN"/ghc --numeric-version && exit || echo yes
eghcup set ${GHC_VERSION} eghcup set ${GHC_VERSION}
eghcup --offline rm 8.10.3 eghcup --offline rm 8.10.3
[ "$(ghc --numeric-version)" = "${ghc_ver}" ] [ "$(ghc --numeric-version)" = "${ghc_ver}" ]
ls -lah "$GHCUP_BIN"
if [ "${OS}" = "DARWIN" ] ; then if [ "${OS}" = "DARWIN" ] ; then
eghcup install hls eghcup install hls
$(eghcup whereis hls) --version $(eghcup whereis hls) --version
@@ -193,18 +173,16 @@ else
eghcup install hls eghcup install hls
haskell-language-server-wrapper --version haskell-language-server-wrapper --version
eghcup unset hls eghcup unset hls
"$GHCUP_BIN"/haskell-language-server-wrapper --version && exit 1 || echo yes "$GHCUP_BIN"/haskell-language-server-wrapper --version && exit || echo yes
eghcup install stack eghcup install stack
stack --version stack --version
eghcup unset stack eghcup unset hls
"$GHCUP_BIN"/stack --version && exit 1 || echo yes "$GHCUP_BIN"/stack --version && exit || echo yes
fi fi
fi fi
fi fi
# check that lazy loading works for 'whereis' # check that lazy loading works for 'whereis'
cp "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml.bak" cp "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml.bak"
echo '**' > "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml" echo '**' > "$CI_PROJECT_DIR/data/metadata/ghcup-${JSON_VERSION}.yaml"
@@ -221,8 +199,6 @@ if [ "${OS}" = "LINUX" ] ; then
fi fi
fi fi
eghcup gc -c
sha_sum() { sha_sum() {
if [ "${OS}" = "FREEBSD" ] ; then if [ "${OS}" = "FREEBSD" ] ; then
sha256 "$@" sha256 "$@"
@@ -270,19 +246,6 @@ if [ "${ARCH}" = "64" ] ; then
eghcup install hls -i "$(pwd)/isolated" 1.3.0 eghcup install hls -i "$(pwd)/isolated" 1.3.0
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0" ] || [ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0" ] ||
[ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0.0" ] [ "$(isolated/haskell-language-server-wrapper --numeric-version)" = "1.3.0.0" ]
# test that isolated installs don't clean up target directory
cat <<EOF > "${GHCUP_BIN}/gmake"
#!/bin/bash
exit 1
EOF
chmod +x "${GHCUP_BIN}/gmake"
mkdir isolated_tainted/
touch isolated_tainted/lol
! eghcup install ghc -i "$(pwd)/isolated_tainted" 8.10.5 --force
[ -e "$(pwd)/isolated_tainted/lol" ]
rm "${GHCUP_BIN}/gmake"
fi fi
fi fi

View File

@@ -15,5 +15,5 @@ git describe
ecabal update ecabal update
ecabal install -w ghc-${GHC_VERSION} --installdir="$CI_PROJECT_DIR"/.local/bin hlint ecabal install -w ghc-${GHC_VERSION} --installdir="$CI_PROJECT_DIR"/.local/bin hlint
hlint -r app/ lib/ test/ hlint -r lib/ test/

90
.gitlab/shell.nix Normal file
View File

@@ -0,0 +1,90 @@
{ system ? "aarch64-darwin"
#, nixpkgs ? fetchTarball https://github.com/angerman/nixpkgs/archive/257cb120334.tar.gz #apple-silicon.tar.gz
, pkgs ? import <nixpkgs> { inherit system; }
, compiler ? if system == "aarch64-darwin" then "ghc8103Binary" else "ghc8103"
}: pkgs.mkShell {
# this prevents nix from trying to write the env-vars file.
# we can't really, as NIX_BUILD_TOP/env-vars is not set.
noDumpEnvVars=1;
# stop polluting LDFLAGS with -liconv
dontAddExtraLibs = true;
# we need to inject ncurses into --with-curses-libraries.
# the real fix is to teach terminfo to use libcurses on macOS.
# CONFIGURE_ARGS = "--with-intree-gmp --with-curses-libraries=${pkgs.ncurses.out}/lib";
CONFIGURE_ARGS = "--with-intree-gmp --with-curses-libraries=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib --with-iconv-includes=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include --with-iconv-libraries=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib SH=/bin/bash";
# magic speedup pony :facepalm:
#
# nix has the ugly habbit of duplicating ld flags more than necessary. This
# somewhat consolidates this.
shellHook = ''
export NIX_LDFLAGS=$(for a in $NIX_LDFLAGS; do echo $a; done |sort|uniq|xargs)
export NIX_LDFLAGS_FOR_TARGET=$(for a in $NIX_LDFLAGS_FOR_TARGET; do echo $a; done |sort|uniq|xargs)
export NIX_LDFLAGS_FOR_TARGET=$(comm -3 <(for l in $NIX_LDFLAGS_FOR_TARGET; do echo $l; done) <(for l in $NIX_LDFLAGS; do echo $l; done))
# Impurity hack for GHC releases.
#################################
# We don't want binary releases to depend on nix, thus we'll need to make sure we don't leak in references.
# GHC externally depends only on iconv and curses. However we can't force a specific curses library for
# the terminfo package, as such we'll need to make sure we only look in the system path for the curses library
# and not pick up the tinfo from the nix provided ncurses package.
#
# We also need to force us to use the systems COREFOUNDATION, not the one that nix builds. Again this is impure,
# but it will allow us to have proper binary distributions.
#
# do not use nixpkgs provided core foundation
export NIX_COREFOUNDATION_RPATH=/System/Library/Frameworks
# drop curses from the LDFLAGS, we really want the system ones, not the nix ones.
export NIX_LDFLAGS=$(for lib in $NIX_LDFLAGS; do case "$lib" in *curses*);; *) echo -n "$lib ";; esac; done;)
export NIX_CFLAGS_COMPILE+=" -Wno-nullability-completeness -Wno-availability -Wno-expansion-to-defined -Wno-builtin-requires-header -Wno-unused-command-line-argument"
# unconditionally add the MacOSX.sdk and TargetConditional.h
export NIX_CFLAGS_COMPILE+=" -isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"
export NIX_LDFLAGS="-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib $NIX_LDFLAGS"
'';
nativeBuildInputs = (with pkgs; [
# This needs to come *before* ghc,
# otherwise we migth end up with the clang from
# the bootstrap GHC in PATH with higher priority.
clang_11
llvm_11
haskell.compiler.${compiler}
haskell.packages.${compiler}.cabal-install
haskell.packages.${compiler}.alex
haskell.packages.${compiler}.happy # _1_19_12 is needed for older GHCs.
automake
autoconf
m4
gmp
zlib.out
zlib.dev
glibcLocales
# locale doesn't build yet :-/
# locale
git
python3
# python3Full
# python3Packages.sphinx
perl
which
wget
curl
file
xz
xlibs.lndir
cacert ])
++ (with pkgs.darwin.apple_sdk.frameworks; [ Foundation Security ]);
}

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "data/metadata"]
path = data/metadata
url = https://github.com/haskell/ghcup-metadata.git
branch = master

View File

@@ -20,7 +20,6 @@
- ignore: {name: "Avoid lambda"} - ignore: {name: "Avoid lambda"}
- ignore: {name: "Use uncurry"} - ignore: {name: "Use uncurry"}
- ignore: {name: "Use replicateM"} - ignore: {name: "Use replicateM"}
- ignore: {name: "Use unless"}
- ignore: {name: "Redundant irrefutable pattern"} - ignore: {name: "Redundant irrefutable pattern"}

View File

@@ -1,59 +1,5 @@
# Revision history for ghcup # Revision history for ghcup
## 0.1.17.8 -- 2022-05-11
* Fix a serious (but hard to trigger) bug when combining `--isolate <DIR>` with `--force`, please make sure to upgrade or avoid `--force`
* Fix HLS build not cleaning up properly on failed installations, fixes [#361](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/361)
* Fix parsing of symlinks with multiple slashes, wrt [#353](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/353)
* Re-enable upgrade functionality for all configurations wrt [MR #250](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/250) and [VSCode haskell issue #601](https://github.com/haskell/vscode-haskell/issues/601)
* Fix `ghcup run --ghc 8.10` (for short versions) wrt [#360](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/360)
- this also introduces a `--quick` switch for `ghcup run`
## 0.1.17.7 -- 2022-04-21
* Fix `ghcup run` on windows wrt [#345](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/345)
## 0.1.17.6 -- 2022-03-18
* Vastly improve shell completions wrt [#242](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/242)
* Fix 'ghcup install cabal/hls/stack --set' wrt [#324](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/324)
* Fix bad error message wrt [#323](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/323)
* Use predictable /tmp names for `ghcup run`, fixes [#329](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/329)
* Fix bug with isolated installation of not previously installed versions
* Add `--no-set` to install commands, fixes [#330](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/330)
* Fix serious bug in `ghcup list --raw-format -t <tool> -c installed`
* Overhaul metadata merging and add `ghcup config add-release-channel URI` wrt [#328](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/328)
* Fix max path issues on windows with `ghcup run`
## 0.1.17.5 -- 2022-02-26
* Implement `ghcup run` subcommand wrt [#137](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/137)
* Support installation of dynamic HLS bindists wrt [HLS #2675](https://github.com/haskell/haskell-language-server/pull/2675) and [#237](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/237)
* Fix XDG support when `~/.local/bin` is a symlink wrt [#311](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/311)
* Add support for quilt-style patches wrt [#230](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/230), by James Hobson
* Fix redundant upgrade warnings in `ghcup upgrade`
* Fix `ghcup whereis ghc` for non-standard versions wrt [#289](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/289)
* Don't print logs to stdout, but stderr
* Allow unpacking legacy lzma archives wrt [#307](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/307)
* Allow to disable self-upgrade functionality wrt [#305](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/305)
* Fix `ghcup install ghc --set` when ghc is already installed wrt [#291](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/291)
## 0.1.17.4 -- 2021-11-13
* add `--metadata-caching` option, allowing to also disable yaml metadata caching wrt [#278](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/278)
* make upgrading ghcup in TUI more pleasant wrt [#276](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/276)
* fix parsing of atypical GHC versions (e.g. `8.10.5-patch1`)
* fix compiling HLS dynamically linked, also see [#245](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/245)
* redo (and break) some of the `ghcup compile <tool>` interface, improving patch options and setting custom cabal.project files
* avoid redundant update warnings wrt [#283](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/283)
## 0.1.17.3 -- 2021-10-27
* clean up during unpack failures as well
* migrate te aeson-2.0.1.0
* switch to yaml-streamly to fix performance regression wrt [#270](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/270)
* use [github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata) for metadata file download (better caching)
## 0.1.17.2 -- 2021-09-30 ## 0.1.17.2 -- 2021-09-30
* Honour GHC bootstrap compiler during git clone stages wrt [#250](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/250) * Honour GHC bootstrap compiler during git clone stages wrt [#250](https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/250)

View File

@@ -9,5 +9,3 @@
GHCup is an installer for the general purpose language [Haskell](https://www.haskell.org/). GHCup is an installer for the general purpose language [Haskell](https://www.haskell.org/).
Visit the [documentation](https://www.haskell.org/ghcup/) for installation instructions. Visit the [documentation](https://www.haskell.org/ghcup/) for installation instructions.
If you're looking for the metadata YAML files, see here: [https://github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata)

157
app/ghcup-gen/Main.hs Normal file
View File

@@ -0,0 +1,157 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import GHCup.Types
import GHCup.Errors
import GHCup.Platform
import GHCup.Utils.Dirs
import GHCup.Utils.Logger
import GHCup.Types.JSON ( )
import Control.Monad.Trans.Reader ( runReaderT )
import Control.Monad.IO.Class
import Data.Char ( toLower )
import Data.Maybe
#if !MIN_VERSION_base(4,13,0)
import Data.Semigroup ( (<>) )
#endif
import Options.Applicative hiding ( style )
import Haskus.Utils.Variant.Excepts
import System.Console.Pretty
import System.Environment
import System.Exit
import System.IO ( stderr )
import Text.Regex.Posix
import Validate
import Text.PrettyPrint.HughesPJClass ( prettyShow )
import qualified Data.Text.IO as T
import qualified Data.Text as T
import qualified Data.ByteString as B
import qualified Data.YAML.Aeson as Y
data Options = Options
{ optCommand :: Command
}
data Command = ValidateYAML ValidateYAMLOpts
| ValidateTarballs ValidateYAMLOpts TarballFilter
data Input
= FileInput FilePath -- optsparse-applicative doesn't handle ByteString correctly anyway
| StdInput
fileInput :: Parser Input
fileInput =
FileInput
<$> strOption
(long "file" <> short 'f' <> metavar "FILENAME" <> help
"Input file to validate"
)
stdInput :: Parser Input
stdInput = flag'
StdInput
(short 'i' <> long "stdin" <> help "Validate from stdin (default)")
inputP :: Parser Input
inputP = fileInput <|> stdInput
data ValidateYAMLOpts = ValidateYAMLOpts
{ vInput :: Maybe Input
}
validateYAMLOpts :: Parser ValidateYAMLOpts
validateYAMLOpts = ValidateYAMLOpts <$> optional inputP
tarballFilterP :: Parser TarballFilter
tarballFilterP = option readm $
long "tarball-filter" <> short 'u' <> metavar "<tool>-<version>" <> value def
<> help "Only check certain tarballs (format: <tool>-<version>)"
where
def = TarballFilter (Right Nothing) (makeRegex ("" :: String))
readm = do
s <- str
case span (/= '-') s of
(_, []) -> fail "invalid format, missing '-' after the tool name"
(t, v) | [tool] <- [ tool | tool <- [minBound..maxBound], low (show tool) == low t ] ->
pure (TarballFilter $ Right $ Just tool) <*> makeRegexOptsM compIgnoreCase execBlank (drop 1 v)
(t, v) | [tool] <- [ tool | tool <- [minBound..maxBound], low (show tool) == low t ] ->
pure (TarballFilter $ Left tool) <*> makeRegexOptsM compIgnoreCase execBlank (drop 1 v)
_ -> fail "invalid tool"
low = fmap toLower
opts :: Parser Options
opts = Options <$> com
com :: Parser Command
com = subparser
( command
"check"
( ValidateYAML
<$> info (validateYAMLOpts <**> helper)
(progDesc "Validate the YAML")
)
<> command
"check-tarballs"
(info
((ValidateTarballs <$> validateYAMLOpts <*> tarballFilterP) <**> helper)
(progDesc "Validate all tarballs (download and checksum)")
)
)
main :: IO ()
main = do
no_color <- isJust <$> lookupEnv "NO_COLOR"
let loggerConfig = LoggerConfig { lcPrintDebug = True
, consoleOutter = T.hPutStr stderr
, fileOutter = \_ -> pure ()
, fancyColors = not no_color
}
dirs <- liftIO getAllDirs
let leanAppstate = LeanAppState (Settings True False Never Curl True GHCupURL False GPGNone False) dirs defaultKeyBindings loggerConfig
pfreq <- (
flip runReaderT leanAppstate . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] $ platformRequest
) >>= \case
VRight r -> pure r
VLeft e -> do
flip runReaderT leanAppstate $ logError $ T.pack $ prettyShow e
liftIO $ exitWith (ExitFailure 2)
let appstate = AppState (Settings True False Never Curl True GHCupURL False GPGNone False) dirs defaultKeyBindings (GHCupInfo mempty mempty mempty) pfreq loggerConfig
_ <- customExecParser (prefs showHelpOnError) (info (opts <**> helper) idm)
>>= \Options {..} -> case optCommand of
ValidateYAML vopts -> withValidateYamlOpts vopts (\dl m -> flip runReaderT appstate $ validate dl m)
ValidateTarballs vopts tarballFilter -> withValidateYamlOpts vopts (\dl m -> flip runReaderT appstate $ validateTarballs tarballFilter dl m)
pure ()
where
withValidateYamlOpts vopts f = case vopts of
ValidateYAMLOpts { vInput = Nothing } ->
B.getContents >>= valAndExit f
ValidateYAMLOpts { vInput = Just StdInput } ->
B.getContents >>= valAndExit f
ValidateYAMLOpts { vInput = Just (FileInput file) } ->
B.readFile file >>= valAndExit f
valAndExit f contents = do
(GHCupInfo _ av gt) <- case Y.decode1Strict contents of
Right r -> pure r
Left (_, e) -> die (color Red $ show e)
f av gt
>>= exitWith

280
app/ghcup-gen/Validate.hs Normal file
View File

@@ -0,0 +1,280 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
module Validate where
import GHCup
import GHCup.Download
import GHCup.Errors
import GHCup.Types
import GHCup.Types.Optics
import GHCup.Utils
import GHCup.Utils.Logger
import GHCup.Utils.Version.QQ
import Codec.Archive
import Control.Applicative
import Control.Exception.Safe
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Reader.Class
import Control.Monad.Trans.Class ( lift )
import Control.Monad.Trans.Reader ( runReaderT )
import Control.Monad.Trans.Resource ( runResourceT
, MonadUnliftIO
)
import Data.Containers.ListUtils ( nubOrd )
import Data.IORef
import Data.List
import Data.Versions
import Haskus.Utils.Variant.Excepts
import Optics
import System.FilePath
import System.Exit
import Text.ParserCombinators.ReadP
import Text.PrettyPrint.HughesPJClass ( prettyShow )
import Text.Regex.Posix
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import qualified Data.Version as V
data ValidationError = InternalError String
deriving Show
instance Exception ValidationError
addError :: (MonadReader (IORef Int) m, MonadIO m, Monad m) => m ()
addError = do
ref <- ask
liftIO $ modifyIORef ref (+ 1)
validate :: (Monad m, MonadReader env m, HasLog env, MonadThrow m, MonadIO m, MonadUnliftIO m)
=> GHCupDownloads
-> M.Map GlobalTool DownloadInfo
-> m ExitCode
validate dls _ = do
ref <- liftIO $ newIORef 0
-- verify binary downloads --
flip runReaderT ref $ do
-- unique tags
forM_ (M.toList dls) $ \(t, _) -> checkUniqueTags t
-- required platforms
forM_ (M.toList dls) $ \(t, versions) ->
forM_ (M.toList versions) $ \(v, vi) ->
forM_ (M.toList $ _viArch vi) $ \(arch, pspecs) -> do
checkHasRequiredPlatforms t v (_viTags vi) arch (M.keys pspecs)
checkGHCVerIsValid
forM_ (M.toList dls) $ \(t, _) -> checkMandatoryTags t
_ <- checkGHCHasBaseVersion
-- exit
e <- liftIO $ readIORef ref
if e > 0
then pure $ ExitFailure e
else do
lift $ logInfo "All good"
pure ExitSuccess
where
checkHasRequiredPlatforms t v tags arch pspecs = do
let v' = prettyVer v
arch' = prettyShow arch
when (notElem (Linux UnknownLinux) pspecs) $ do
lift $ logError $
"Linux UnknownLinux missing for for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack arch'
addError
when ((notElem Darwin pspecs) && arch == A_64) $ do
lift $ logError $ "Darwin missing for for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack arch'
addError
when ((notElem FreeBSD pspecs) && arch == A_64) $ lift $ logWarn $
"FreeBSD missing for for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack arch'
when (notElem Windows pspecs && arch == A_64) $ do
lift $ logError $ "Windows missing for for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack arch'
addError
-- alpine needs to be set explicitly, because
-- we cannot assume that "Linux UnknownLinux" runs on Alpine
-- (although it could be static)
when (notElem (Linux Alpine) pspecs) $
case t of
GHCup | arch `elem` [A_64, A_32] -> lift (logError $ "Linux Alpine missing for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack (prettyShow arch)) >> addError
Cabal | v > [vver|2.4.1.0|]
, arch `elem` [A_64, A_32] -> lift (logError $ "Linux Alpine missing for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack (prettyShow arch)) >> addError
GHC | Latest `elem` tags || Recommended `elem` tags
, arch `elem` [A_64, A_32] -> lift (logError $ "Linux Alpine missing for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack (prettyShow arch))
_ -> lift $ logWarn $ "Linux Alpine missing for " <> T.pack (prettyShow t) <> " " <> v' <> " " <> T.pack (prettyShow arch)
checkUniqueTags tool = do
let allTags = join $ fmap _viTags $ M.elems $ availableToolVersions dls tool
let nonUnique =
fmap fst
. filter (\(_, b) -> not b)
<$> ( mapM
(\case
[] -> throwM $ InternalError "empty inner list"
(t : ts) ->
pure $ (t, ) (not (isUniqueTag t) || null ts)
)
. group
. sort
$ allTags
)
case join nonUnique of
[] -> pure ()
xs -> do
lift $ logError $ "Tags not unique for " <> T.pack (prettyShow tool) <> ": " <> T.pack (prettyShow xs)
addError
where
isUniqueTag Latest = True
isUniqueTag Recommended = True
isUniqueTag Old = False
isUniqueTag Prerelease = False
isUniqueTag (Base _) = False
isUniqueTag (UnknownTag _) = False
checkGHCVerIsValid = do
let ghcVers = toListOf (ix GHC % to M.keys % folded) dls
forM_ ghcVers $ \v ->
case [ x | (x,"") <- readP_to_S V.parseVersion (T.unpack . prettyVer $ v) ] of
[_] -> pure ()
_ -> do
lift $ logError $ "GHC version " <> prettyVer v <> " is not valid"
addError
-- a tool must have at least one of each mandatory tags
checkMandatoryTags tool = do
let allTags = join $ fmap _viTags $ M.elems $ availableToolVersions dls tool
forM_ [Latest, Recommended] $ \t -> case elem t allTags of
False -> do
lift $ logError $ "Tag " <> T.pack (prettyShow t) <> " missing from " <> T.pack (prettyShow tool)
addError
True -> pure ()
-- all GHC versions must have a base tag
checkGHCHasBaseVersion = do
let allTags = M.toList $ availableToolVersions dls GHC
forM allTags $ \(ver, _viTags -> tags) -> case any isBase tags of
False -> do
lift $ logError $ "Base tag missing from GHC ver " <> prettyVer ver
addError
True -> pure ()
isBase (Base _) = True
isBase _ = False
data TarballFilter = TarballFilter
{ tfTool :: Either GlobalTool (Maybe Tool)
, tfVersion :: Regex
}
validateTarballs :: ( Monad m
, MonadReader env m
, HasLog env
, HasDirs env
, HasSettings env
, MonadThrow m
, MonadIO m
, MonadUnliftIO m
, MonadMask m
, Alternative m
, MonadFail m
)
=> TarballFilter
-> GHCupDownloads
-> M.Map GlobalTool DownloadInfo
-> m ExitCode
validateTarballs (TarballFilter etool versionRegex) dls gt = do
ref <- liftIO $ newIORef 0
-- download/verify all tarballs
let dlis = either (const []) (\tool -> nubOrd $ dls ^.. each %& indices (maybe (const True) (==) tool) %> each %& indices (matchTest versionRegex . T.unpack . prettyVer) % (viSourceDL % _Just `summing` viArch % each % each % each)) etool
let gdlis = nubOrd $ gt ^.. each
let allDls = either (const gdlis) (const dlis) etool
when (null allDls) $ logError "no tarballs selected by filter" *> (flip runReaderT ref addError)
forM_ allDls (downloadAll ref)
-- exit
e <- liftIO $ readIORef ref
if e > 0
then pure $ ExitFailure e
else do
logInfo "All good"
pure ExitSuccess
where
downloadAll :: ( MonadUnliftIO m
, MonadIO m
, MonadReader env m
, HasLog env
, HasDirs env
, HasSettings env
, MonadCatch m
, MonadMask m
, MonadThrow m
)
=> IORef Int
-> DownloadInfo
-> m ()
downloadAll ref dli = do
r <- runResourceT
. runE @'[DigestError
, GPGError
, DownloadFailed
, UnknownArchive
, ArchiveResult
]
$ do
case etool of
Right (Just GHCup) -> do
tmpUnpack <- lift mkGhcupTmpDir
_ <- liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmpUnpack Nothing False
pure Nothing
Right _ -> do
p <- liftE $ downloadCached dli Nothing
fmap (Just . head . splitDirectories . head)
. liftE
. getArchiveFiles
$ p
Left ShimGen -> do
tmpUnpack <- lift mkGhcupTmpDir
_ <- liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmpUnpack Nothing False
pure Nothing
case r of
VRight (Just basePath) -> do
case _dlSubdir dli of
Just (RealDir prel) -> do
logInfo
$ " verifying subdir: " <> T.pack prel
when (basePath /= prel) $ do
logError $
"Subdir doesn't match: expected " <> T.pack prel <> ", got " <> T.pack basePath
(flip runReaderT ref addError)
Just (RegexDir regexString) -> do
logInfo $
"verifying subdir (regex): " <> T.pack regexString
let regex = makeRegexOpts
compIgnoreCase
execBlank
regexString
when (not (match regex basePath)) $ do
logError $
"Subdir doesn't match: expected regex " <> T.pack regexString <> ", got " <> T.pack basePath
(flip runReaderT ref addError)
Nothing -> pure ()
VRight Nothing -> pure ()
VLeft e -> do
logError $
"Could not download (or verify hash) of " <> T.pack (show dli) <> ", Error was: " <> T.pack (prettyShow e)
(flip runReaderT ref addError)

View File

@@ -2,7 +2,10 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
module BrickMain where module BrickMain where
@@ -10,7 +13,6 @@ module BrickMain where
import GHCup import GHCup
import GHCup.Download import GHCup.Download
import GHCup.Errors import GHCup.Errors
import GHCup.Types.Optics ( getDirs )
import GHCup.Types hiding ( LeanAppState(..) ) import GHCup.Types hiding ( LeanAppState(..) )
import GHCup.Utils import GHCup.Utils
import GHCup.Utils.Logger import GHCup.Utils.Logger
@@ -27,9 +29,6 @@ import Brick.Widgets.List ( listSelectedFocusedAttr
) )
import Codec.Archive import Codec.Archive
import Control.Exception.Safe import Control.Exception.Safe
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
@@ -44,7 +43,6 @@ import Data.Vector ( Vector
import Data.Versions hiding ( str ) import Data.Versions hiding ( str )
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Prelude hiding ( appendFile ) import Prelude hiding ( appendFile )
import System.FilePath
import System.Exit import System.Exit
import System.IO.Unsafe import System.IO.Unsafe
import Text.PrettyPrint.HughesPJClass ( prettyShow ) import Text.PrettyPrint.HughesPJClass ( prettyShow )
@@ -53,8 +51,6 @@ import URI.ByteString
import qualified Data.Text as T import qualified Data.Text as T
import qualified Graphics.Vty as Vty import qualified Graphics.Vty as Vty
import qualified Data.Vector as V import qualified Data.Vector as V
import System.Environment (getExecutablePath)
import qualified System.Posix.Process as SPP
hiddenTools :: [Tool] hiddenTools :: [Tool]
@@ -372,7 +368,10 @@ listSelectedElement' BrickInternalState{..} = fmap (ix, ) $ clr !? ix
selectLatest :: Vector ListResult -> Int selectLatest :: Vector ListResult -> Int
selectLatest = fromMaybe 0 . V.findIndex (\ListResult {..} -> lTool == GHC && Latest `elem` lTag) selectLatest v =
case V.findIndex (\ListResult {..} -> lTool == GHC && Latest `elem` lTag) v of
Just ix -> ix
Nothing -> 0
-- | Replace the @appState@ or construct it based on a filter function -- | Replace the @appState@ or construct it based on a filter function
@@ -399,14 +398,14 @@ filterVisible :: Bool -> Bool -> ListResult -> Bool
filterVisible v t e | lInstalled e = True filterVisible v t e | lInstalled e = True
| v | v
, not t , not t
, lTool e `notElem` hiddenTools = True , not (elem (lTool e) hiddenTools) = True
| not v | not v
, t , t
, Old `notElem` lTag e = True , not (elem Old (lTag e)) = True
| v | v
, t = True , t = True
| otherwise = (Old `notElem` lTag e) && | otherwise = not (elem Old (lTag e)) &&
(lTool e `notElem` hiddenTools) not (elem (lTool e) hiddenTools)
install' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m) install' :: (MonadReader AppState m, MonadIO m, MonadThrow m, MonadFail m, MonadMask m, MonadUnliftIO m)
@@ -436,47 +435,30 @@ install' _ (_, ListResult {..}) = do
, TarDirDoesNotExist , TarDirDoesNotExist
, FileAlreadyExistsError , FileAlreadyExistsError
, ProcessError , ProcessError
, GHCupShadowed
, UninstallFailed
] ]
run (do run (do
ce <- liftIO $ fmap (either (const Nothing) Just) $
try @_ @SomeException $ getExecutablePath >>= canonicalizePath
dirs <- lift getDirs
case lTool of case lTool of
GHC -> do GHC -> do
let vi = getVersionInfo lVer GHC dls let vi = getVersionInfo lVer GHC dls
liftE $ installGHCBin lVer GHCupInternal False $> (vi, dirs, ce) liftE $ installGHCBin lVer Nothing False $> vi
Cabal -> do Cabal -> do
let vi = getVersionInfo lVer Cabal dls let vi = getVersionInfo lVer Cabal dls
liftE $ installCabalBin lVer GHCupInternal False $> (vi, dirs, ce) liftE $ installCabalBin lVer Nothing False $> vi
GHCup -> do GHCup -> do
let vi = snd <$> getLatest dls GHCup let vi = snd <$> getLatest dls GHCup
liftE $ upgradeGHCup Nothing False False $> (vi, dirs, ce) liftE $ upgradeGHCup Nothing False $> vi
HLS -> do HLS -> do
let vi = getVersionInfo lVer HLS dls let vi = getVersionInfo lVer HLS dls
liftE $ installHLSBin lVer GHCupInternal False $> (vi, dirs, ce) liftE $ installHLSBin lVer Nothing False $> vi
Stack -> do Stack -> do
let vi = getVersionInfo lVer Stack dls let vi = getVersionInfo lVer Stack dls
liftE $ installStackBin lVer GHCupInternal False $> (vi, dirs, ce) liftE $ installStackBin lVer Nothing False $> vi
) )
>>= \case >>= \case
VRight (vi, Dirs{..}, Just ce) -> do VRight vi -> do
forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg forM_ (_viPostInstall =<< vi) $ \msg ->
case lTool of logInfo msg
GHCup -> do
up <- liftIO $ fmap (either (const Nothing) Just)
$ try @_ @SomeException $ canonicalizePath (binDir </> "ghcup" <.> exeExt)
when ((normalise <$> up) == Just (normalise ce)) $
-- TODO: track cli arguments of previous invocation
liftIO $ SPP.executeFile ce False ["tui"] Nothing
logInfo "Please restart 'ghcup' for the changes to take effect"
_ -> pure ()
pure $ Right ()
VRight (vi, _, _) -> do
forM_ (_viPostInstall =<< vi) $ \msg -> logInfo msg
logInfo "Please restart 'ghcup' for the changes to take effect"
pure $ Right () pure $ Right ()
VLeft (V (AlreadyInstalled _ _)) -> pure $ Right () VLeft (V (AlreadyInstalled _ _)) -> pure $ Right ()
VLeft (V NoUpdate) -> pure $ Right () VLeft (V NoUpdate) -> pure $ Right ()
@@ -494,9 +476,9 @@ set' _ (_, ListResult {..}) = do
run (do run (do
case lTool of case lTool of
GHC -> liftE $ setGHC (GHCTargetVersion lCross lVer) SetGHCOnly Nothing $> () GHC -> liftE $ setGHC (GHCTargetVersion lCross lVer) SetGHCOnly $> ()
Cabal -> liftE $ setCabal lVer $> () Cabal -> liftE $ setCabal lVer $> ()
HLS -> liftE $ setHLS lVer SetHLSOnly Nothing $> () HLS -> liftE $ setHLS lVer $> ()
Stack -> liftE $ setStack lVer $> () Stack -> liftE $ setStack lVer $> ()
GHCup -> pure () GHCup -> pure ()
) )
@@ -512,7 +494,7 @@ del' :: (MonadReader AppState m, MonadIO m, MonadFail m, MonadMask m, MonadUnlif
del' _ (_, ListResult {..}) = do del' _ (_, ListResult {..}) = do
AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask AppState { ghcupInfo = GHCupInfo { _ghcupDownloads = dls }} <- ask
let run = runE @'[NotInstalled, UninstallFailed] let run = runE @'[NotInstalled]
run (do run (do
let vi = getVersionInfo lVer lTool dls let vi = getVersionInfo lVer lTool dls
@@ -525,7 +507,7 @@ del' _ (_, ListResult {..}) = do
) )
>>= \case >>= \case
VRight vi -> do VRight vi -> do
forM_ (_viPostRemove =<< vi) $ \msg -> forM_ (join $ fmap _viPostRemove vi) $ \msg ->
logInfo msg logInfo msg
pure $ Right () pure $ Right ()
VLeft e -> pure $ Left (prettyShow e) VLeft e -> pure $ Left (prettyShow e)
@@ -560,7 +542,17 @@ settings' = unsafePerformIO $ do
, fileOutter = \_ -> pure () , fileOutter = \_ -> pure ()
, fancyColors = True , fancyColors = True
} }
newIORef $ AppState defaultSettings newIORef $ AppState (Settings { cache = True
, noVerify = False
, keepDirs = Never
, downloader = Curl
, verbose = False
, urlSource = GHCupURL
, noNetwork = False
, gpgSetting = GPGNone
, noColor = False
, ..
})
dirs dirs
defaultKeyBindings defaultKeyBindings
(GHCupInfo mempty mempty mempty) (GHCupInfo mempty mempty mempty)
@@ -602,7 +594,8 @@ getGHCupInfo = do
r <- r <-
flip runReaderT settings flip runReaderT settings
. runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError] . runE @'[DigestError, GPGError, JSONError , DownloadFailed , FileDoesNotExistError]
$ liftE getDownloadsF $ liftE
$ getDownloadsF
case r of case r of
VRight a -> pure $ Right a VRight a -> pure $ Right a
@@ -619,3 +612,4 @@ getAppData mgi = runExceptT $ do
flip runReaderT settings $ do flip runReaderT settings $ do
lV <- listVersions Nothing Nothing lV <- listVersions Nothing Nothing
pure $ BrickData (reverse lV) pure $ BrickData (reverse lV)

View File

@@ -1,7 +1,11 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
@@ -15,16 +19,13 @@ module GHCup.OptParse (
, module GHCup.OptParse.Config , module GHCup.OptParse.Config
, module GHCup.OptParse.Whereis , module GHCup.OptParse.Whereis
, module GHCup.OptParse.List , module GHCup.OptParse.List
#ifndef DISABLE_UPGRADE
, module GHCup.OptParse.Upgrade , module GHCup.OptParse.Upgrade
#endif
, module GHCup.OptParse.ChangeLog , module GHCup.OptParse.ChangeLog
, module GHCup.OptParse.Prefetch , module GHCup.OptParse.Prefetch
, module GHCup.OptParse.GC , module GHCup.OptParse.GC
, module GHCup.OptParse.DInfo , module GHCup.OptParse.DInfo
, module GHCup.OptParse.Nuke , module GHCup.OptParse.Nuke
, module GHCup.OptParse.ToolRequirements , module GHCup.OptParse.ToolRequirements
, module GHCup.OptParse.Run
, module GHCup.OptParse , module GHCup.OptParse
) where ) where
@@ -34,14 +35,11 @@ import GHCup.OptParse.Install
import GHCup.OptParse.Set import GHCup.OptParse.Set
import GHCup.OptParse.UnSet import GHCup.OptParse.UnSet
import GHCup.OptParse.Rm import GHCup.OptParse.Rm
import GHCup.OptParse.Run
import GHCup.OptParse.Compile import GHCup.OptParse.Compile
import GHCup.OptParse.Config import GHCup.OptParse.Config
import GHCup.OptParse.Whereis import GHCup.OptParse.Whereis
import GHCup.OptParse.List import GHCup.OptParse.List
#ifndef DISABLE_UPGRADE
import GHCup.OptParse.Upgrade import GHCup.OptParse.Upgrade
#endif
import GHCup.OptParse.ChangeLog import GHCup.OptParse.ChangeLog
import GHCup.OptParse.Prefetch import GHCup.OptParse.Prefetch
import GHCup.OptParse.GC import GHCup.OptParse.GC
@@ -73,7 +71,6 @@ data Options = Options
-- global options -- global options
optVerbose :: Maybe Bool optVerbose :: Maybe Bool
, optCache :: Maybe Bool , optCache :: Maybe Bool
, optMetaCache :: Maybe Integer
, optUrlSource :: Maybe URI , optUrlSource :: Maybe URI
, optNoVerify :: Maybe Bool , optNoVerify :: Maybe Bool
, optKeepDirs :: Maybe KeepDirs , optKeepDirs :: Maybe KeepDirs
@@ -95,10 +92,8 @@ data Command
| Compile CompileCommand | Compile CompileCommand
| Config ConfigCommand | Config ConfigCommand
| Whereis WhereisOptions WhereisCommand | Whereis WhereisOptions WhereisCommand
#ifndef DISABLE_UPGRADE | Upgrade UpgradeOpts Bool
| Upgrade UpgradeOpts Bool Bool | ToolRequirements
#endif
| ToolRequirements ToolReqOpts
| ChangeLog ChangeLogOptions | ChangeLog ChangeLogOptions
| Nuke | Nuke
#if defined(BRICK) #if defined(BRICK)
@@ -106,17 +101,15 @@ data Command
#endif #endif
| Prefetch PrefetchCommand | Prefetch PrefetchCommand
| GC GCOptions | GC GCOptions
| Run RunOptions
opts :: Parser Options opts :: Parser Options
opts = opts =
Options Options
<$> invertableSwitch "verbose" (Just 'v') False (help "Enable verbosity (default: disabled)") <$> invertableSwitch "verbose" 'v' False (help "Enable verbosity (default: disabled)")
<*> invertableSwitch "cache" (Just 'c') False (help "Cache downloads in ~/.ghcup/cache (default: disabled)") <*> invertableSwitch "cache" 'c' False (help "Cache downloads in ~/.ghcup/cache (default: disabled)")
<*> optional (option auto (long "metadata-caching" <> help "How long the yaml metadata caching interval is (in seconds), 0 to disable" <> internal)) <*> (optional
<*> optional
(option (option
(eitherReader parseUri) (eitherReader parseUri)
( short 's' ( short 's'
@@ -124,10 +117,10 @@ opts =
<> metavar "URL" <> metavar "URL"
<> help "Alternative ghcup download info url" <> help "Alternative ghcup download info url"
<> internal <> internal
<> completer fileUri
) )
) )
<*> (fmap . fmap) not (invertableSwitch "verify" (Just 'n') True (help "Disable tarball checksum verification (default: enabled)")) )
<*> (fmap . fmap) not (invertableSwitch "verify" 'n' True (help "Disable tarball checksum verification (default: enabled)"))
<*> optional (option <*> optional (option
(eitherReader keepOnParser) (eitherReader keepOnParser)
( long "keep" ( long "keep"
@@ -135,7 +128,6 @@ opts =
<> help <> help
"Keep build directories? (default: errors)" "Keep build directories? (default: errors)"
<> hidden <> hidden
<> completer (listCompleter ["always", "errors", "never"])
)) ))
<*> optional (option <*> optional (option
(eitherReader downloaderParser) (eitherReader downloaderParser)
@@ -144,23 +136,20 @@ opts =
<> metavar "<internal|curl|wget>" <> metavar "<internal|curl|wget>"
<> help <> help
"Downloader to use (default: internal)" "Downloader to use (default: internal)"
<> completer (listCompleter ["internal", "curl", "wget"])
#else #else
<> metavar "<curl|wget>" <> metavar "<curl|wget>"
<> help <> help
"Downloader to use (default: curl)" "Downloader to use (default: curl)"
<> completer (listCompleter ["curl", "wget"])
#endif #endif
<> hidden <> hidden
)) ))
<*> invertableSwitch "offline" (Just 'o') False (help "Don't do any network calls, trying cached assets and failing if missing.") <*> invertableSwitch "offline" 'o' False (help "Don't do any network calls, trying cached assets and failing if missing.")
<*> optional (option <*> optional (option
(eitherReader gpgParser) (eitherReader gpgParser)
( long "gpg" ( long "gpg"
<> metavar "<strict|lax|none>" <> metavar "<strict|lax|none>"
<> help <> help
"GPG verification (default: none)" "GPG verification (default: none)"
<> completer (listCompleter ["strict", "lax", "none"])
)) ))
<*> com <*> com
where where
@@ -175,10 +164,11 @@ com =
( command ( command
"tui" "tui"
( (\_ -> Interactive) ( (\_ -> Interactive)
<$> info <$> (info
helper helper
( progDesc "Start the interactive GHCup UI" ( progDesc "Start the interactive GHCup UI"
) )
)
) )
<> command <> command
#else #else
@@ -227,8 +217,6 @@ com =
(info (info
( (Upgrade <$> upgradeOptsP <*> switch ( (Upgrade <$> upgradeOptsP <*> switch
(short 'f' <> long "force" <> help "Force update") (short 'f' <> long "force" <> help "Force update")
<*> switch
(long "fail-if-shadowed" <> help "Fails after upgrading if the upgraded ghcup binary is shadowed by something else in PATH (useful for CI)")
) )
<**> helper <**> helper
) )
@@ -271,16 +259,6 @@ com =
(progDesc "Garbage collection" (progDesc "Garbage collection"
<> footerDoc ( Just $ text gcFooter )) <> footerDoc ( Just $ text gcFooter ))
) )
<> command
"run"
(Run
<$>
info
(runOpts <**> helper)
(progDesc "Run a command with the given tool in PATH"
<> footerDoc ( Just $ text runFooter )
)
)
<> commandGroup "Main commands:" <> commandGroup "Main commands:"
) )
<|> subparser <|> subparser
@@ -289,8 +267,8 @@ com =
((\_ -> DInfo) <$> info helper (progDesc "Show debug info")) ((\_ -> DInfo) <$> info helper (progDesc "Show debug info"))
<> command <> command
"tool-requirements" "tool-requirements"
( ToolRequirements ( (\_ -> ToolRequirements)
<$> info (toolReqP <**> helper) <$> info helper
(progDesc "Show the requirements for ghc/cabal") (progDesc "Show the requirements for ghc/cabal")
) )
<> command <> command

View File

@@ -2,6 +2,7 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
@@ -76,7 +77,6 @@ changelogP =
) )
(short 't' <> long "tool" <> metavar "<ghc|cabal|ghcup>" <> help (short 't' <> long "tool" <> metavar "<ghc|cabal|ghcup>" <> help
"Open changelog for given tool (default: ghc)" "Open changelog for given tool (default: ghc)"
<> completer toolCompleter
) )
) )
<*> optional (toolVersionArgument Nothing Nothing) <*> optional (toolVersionArgument Nothing Nothing)

View File

@@ -1,10 +1,12 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE NumericUnderscores #-}
module GHCup.OptParse.Common where module GHCup.OptParse.Common where
@@ -16,54 +18,36 @@ import GHCup.Platform
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
import GHCup.Utils import GHCup.Utils
import GHCup.Utils.File
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.MegaParsec import GHCup.Utils.MegaParsec
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import Control.DeepSeq
import Control.Concurrent
import Control.Concurrent.Async
import Control.Exception.Safe import Control.Exception.Safe
#if !MIN_VERSION_base(4,13,0) #if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail ) import Control.Monad.Fail ( MonadFail )
#endif #endif
import Control.Monad.Reader import Control.Monad.Reader
import Data.Aeson
#if MIN_VERSION_aeson(2,0,0)
import qualified Data.Aeson.Key as KM
import qualified Data.Aeson.KeyMap as KM
#else
import qualified Data.HashMap.Strict as KM
#endif
import Data.ByteString.Lazy ( ByteString )
import Data.Bifunctor import Data.Bifunctor
import Data.Char import Data.Char
import Data.Either import Data.Either
import Data.Functor import Data.Functor
import Data.List ( nub, sort, sortBy, isPrefixOf, stripPrefix ) import Data.List ( nub, sort, sortBy )
import Data.Maybe import Data.Maybe
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Versions hiding ( str ) import Data.Versions hiding ( str )
import Data.Void import Data.Void
import qualified Data.Vector as V
import GHC.IO.Exception
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Options.Applicative hiding ( style ) import Options.Applicative hiding ( style )
import Prelude hiding ( appendFile ) import Prelude hiding ( appendFile )
import Safe import Safe
import System.Process ( readProcess )
import System.FilePath import System.FilePath
import Text.HTML.TagSoup hiding ( Tag )
import URI.ByteString import URI.ByteString
import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.ByteString.UTF8 as UTF8
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
import qualified Text.Megaparsec as MP import qualified Text.Megaparsec as MP
import qualified System.FilePath.Posix as FP
import GHCup.Version import GHCup.Version
import Control.Exception (evaluate)
------------- -------------
@@ -137,7 +121,7 @@ versionArgument criteria tool = argument (eitherReader tVersionEither) (metavar
-- the help is shown only for --no-recursive. -- the help is shown only for --no-recursive.
invertableSwitch invertableSwitch
:: String -- ^ long option :: String -- ^ long option
-> Maybe Char -- ^ short option for the non-default option -> Char -- ^ short option for the non-default option
-> Bool -- ^ is switch enabled by default? -> Bool -- ^ is switch enabled by default?
-> Mod FlagFields Bool -- ^ option modifier -> Mod FlagFields Bool -- ^ option modifier
-> Parser (Maybe Bool) -> Parser (Maybe Bool)
@@ -148,14 +132,14 @@ invertableSwitch longopt shortopt defv optmod = invertableSwitch' longopt shorto
-- | Allows providing option modifiers for both --foo and --no-foo. -- | Allows providing option modifiers for both --foo and --no-foo.
invertableSwitch' invertableSwitch'
:: String -- ^ long option (eg "foo") :: String -- ^ long option (eg "foo")
-> Maybe Char -- ^ short option for the non-default option -> Char -- ^ short option for the non-default option
-> Bool -- ^ is switch enabled by default? -> Bool -- ^ is switch enabled by default?
-> Mod FlagFields Bool -- ^ option modifier for --foo -> Mod FlagFields Bool -- ^ option modifier for --foo
-> Mod FlagFields Bool -- ^ option modifier for --no-foo -> Mod FlagFields Bool -- ^ option modifier for --no-foo
-> Parser (Maybe Bool) -> Parser (Maybe Bool)
invertableSwitch' longopt shortopt defv enmod dismod = optional invertableSwitch' longopt shortopt defv enmod dismod = optional
( flag' True ( enmod <> long longopt <> if defv then mempty else maybe mempty short shortopt) ( flag' True ( enmod <> long longopt <> if defv then mempty else short shortopt)
<|> flag' False (dismod <> long nolongopt <> if defv then maybe mempty short shortopt else mempty) <|> flag' False (dismod <> long nolongopt <> if defv then short shortopt else mempty)
) )
where where
nolongopt = "no-" ++ longopt nolongopt = "no-" ++ longopt
@@ -176,7 +160,7 @@ platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
archP = MP.try (MP.chunk "x86_64" $> A_64) <|> (MP.chunk "i386" $> A_32) archP = MP.try (MP.chunk "x86_64" $> A_64) <|> (MP.chunk "i386" $> A_32)
platformP :: MP.Parsec Void Text PlatformRequest platformP :: MP.Parsec Void Text PlatformRequest
platformP = choice' platformP = choice'
[ (`PlatformRequest` FreeBSD) [ (\a mv -> PlatformRequest a FreeBSD mv)
<$> (archP <* MP.chunk "-") <$> (archP <* MP.chunk "-")
<*> ( MP.chunk "portbld" <*> ( MP.chunk "portbld"
*> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof)) *> ( MP.try (Just <$> verP (MP.chunk "-freebsd" <* MP.eof))
@@ -184,7 +168,7 @@ platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
) )
<* MP.chunk "-freebsd" <* MP.chunk "-freebsd"
) )
, (`PlatformRequest` Darwin) , (\a mv -> PlatformRequest a Darwin mv)
<$> (archP <* MP.chunk "-") <$> (archP <* MP.chunk "-")
<*> ( MP.chunk "apple" <*> ( MP.chunk "apple"
*> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof)) *> ( MP.try (Just <$> verP (MP.chunk "-darwin" <* MP.eof))
@@ -216,8 +200,8 @@ platformParser s' = case MP.parse (platformP <* MP.eof) "" (T.pack s') of
] ]
uriParser :: String -> Either String URI bindistParser :: String -> Either String URI
uriParser = first show . parseURI strictURIParserOptions . UTF8.fromString bindistParser = first show . parseURI strictURIParserOptions . UTF8.fromString
absolutePathParser :: FilePath -> Either String FilePath absolutePathParser :: FilePath -> Either String FilePath
@@ -266,6 +250,18 @@ criteriaParser s' | t == T.pack "installed" = Right ListInstalled
where t = T.toLower (T.pack s') where t = T.toLower (T.pack s')
toolVersionParser :: Parser ToolVersion
toolVersionParser = verP' <|> toolP
where
verP' = ToolVersion <$> versionParser
toolP =
ToolTag
<$> option
(eitherReader tagEither)
(short 't' <> long "tag" <> metavar "TAG" <> help "The target tag")
keepOnParser :: String -> Either String KeepDirs keepOnParser :: String -> Either String KeepDirs
keepOnParser s' | t == T.pack "always" = Right Always keepOnParser s' | t == T.pack "always" = Right Always
@@ -297,126 +293,6 @@ gpgParser s' | t == T.pack "strict" = Right GPGStrict
--[ Completers ]-- --[ Completers ]--
------------------ ------------------
toolCompleter :: Completer
toolCompleter = listCompleter ["ghc", "cabal", "hls", "stack"]
gitFileUri :: [String] -> Completer
gitFileUri add = mkCompleter $ fileUri' (["git://"] <> add)
fileUri :: Completer
fileUri = mkCompleter $ fileUri' []
fileUri' :: [String] -> String -> IO [String]
fileUri' add = \case
"" -> do
pwd <- getCurrentDirectory
pure $ ["https://", "http://", "file:///", "file://" <> pwd <> "/"] <> add
xs
| "file:///" `isPrefixOf` xs -> fmap ("file://" <>) <$>
case stripPrefix "file://" xs of
Nothing -> pure []
Just r -> do
pwd <- getCurrentDirectory
dirs <- compgen "directory" r ["-S", "/"]
files <- filter (\f -> (f <> "/") `notElem` dirs) <$> compgen "file" r []
pure (dirs <> files <> if r `isPrefixOf` pwd then [pwd <> "/"] else [])
| xs `isPrefixOf` "file:///" -> pure ["file:///"]
| xs `isPrefixOf` "https://" -> pure ["https://"]
| xs `isPrefixOf` "http://" -> pure ["http://"]
| otherwise -> pure []
where
compgen :: String -> String -> [String] -> IO [String]
compgen action' r opts = do
let cmd = unwords $ ["compgen", "-A", action'] <> opts <> ["--", requote r]
result <- tryIO $ readProcess "bash" ["-c", cmd] ""
return . lines . either (const []) id $ result
-- | Strongly quote the string we pass to compgen.
--
-- We need to do this so bash doesn't expand out any ~ or other
-- chars we want to complete on, or emit an end of line error
-- when seeking the close to the quote.
--
-- NOTE: copied from https://hackage.haskell.org/package/optparse-applicative-0.17.0.0/docs/src/Options.Applicative.Builder.Completer.html#requote
requote :: String -> String
requote s =
let
-- Bash doesn't appear to allow "mixed" escaping
-- in bash completions. So we don't have to really
-- worry about people swapping between strong and
-- weak quotes.
unescaped =
case s of
-- It's already strongly quoted, so we
-- can use it mostly as is, but we must
-- ensure it's closed off at the end and
-- there's no single quotes in the
-- middle which might confuse bash.
('\'': rs) -> unescapeN rs
-- We're weakly quoted.
('"': rs) -> unescapeD rs
-- We're not quoted at all.
-- We need to unescape some characters like
-- spaces and quotation marks.
elsewise -> unescapeU elsewise
in
strong unescaped
where
strong ss = '\'' : foldr go "'" ss
where
-- If there's a single quote inside the
-- command: exit from the strong quote and
-- emit it the quote escaped, then resume.
go '\'' t = "'\\''" ++ t
go h t = h : t
-- Unescape a strongly quoted string
-- We have two recursive functions, as we
-- can enter and exit the strong escaping.
unescapeN = goX
where
goX ('\'' : xs) = goN xs
goX (x : xs) = x : goX xs
goX [] = []
goN ('\\' : '\'' : xs) = '\'' : goN xs
goN ('\'' : xs) = goX xs
goN (x : xs) = x : goN xs
goN [] = []
-- Unescape an unquoted string
unescapeU = goX
where
goX [] = []
goX ('\\' : x : xs) = x : goX xs
goX (x : xs) = x : goX xs
-- Unescape a weakly quoted string
unescapeD = goX
where
-- Reached an escape character
goX ('\\' : x : xs)
-- If it's true escapable, strip the
-- slashes, as we're going to strong
-- escape instead.
| x `elem` ("$`\"\\\n" :: String) = x : goX xs
| otherwise = '\\' : x : goX xs
-- We've ended quoted section, so we
-- don't recurse on goX, it's done.
goX ('"' : xs)
= xs
-- Not done, but not a special character
-- just continue the fold.
goX (x : xs)
= x : goX xs
goX []
= []
tagCompleter :: Tool -> [String] -> Completer tagCompleter :: Tool -> [String] -> Completer
tagCompleter tool add = listIOCompleter $ do tagCompleter tool add = listIOCompleter $ do
dirs' <- liftIO getAllDirs dirs' <- liftIO getAllDirs
@@ -427,7 +303,7 @@ tagCompleter tool add = listIOCompleter $ do
, fancyColors = False , fancyColors = False
} }
let appState = LeanAppState let appState = LeanAppState
(defaultSettings { noNetwork = True }) (Settings True False Never Curl False GHCupURL True GPGNone False)
dirs' dirs'
defaultKeyBindings defaultKeyBindings
loggerConfig loggerConfig
@@ -435,8 +311,11 @@ tagCompleter tool add = listIOCompleter $ do
mGhcUpInfo <- flip runReaderT appState . runE $ getDownloadsF mGhcUpInfo <- flip runReaderT appState . runE $ getDownloadsF
case mGhcUpInfo of case mGhcUpInfo of
VRight ghcupInfo -> do VRight ghcupInfo -> do
let allTags = filter (/= Old) let allTags = filter (\t -> t /= Old)
$ _viTags =<< M.elems (availableToolVersions (_ghcupDownloads ghcupInfo) tool) $ join
$ fmap _viTags
$ M.elems
$ availableToolVersions (_ghcupDownloads ghcupInfo) tool
pure $ nub $ (add ++) $ fmap tagToString allTags pure $ nub $ (add ++) $ fmap tagToString allTags
VLeft _ -> pure (nub $ ["recommended", "latest"] ++ add) VLeft _ -> pure (nub $ ["recommended", "latest"] ++ add)
@@ -450,7 +329,7 @@ versionCompleter criteria tool = listIOCompleter $ do
, fileOutter = mempty , fileOutter = mempty
, fancyColors = False , fancyColors = False
} }
let settings = defaultSettings { noNetwork = True } let settings = Settings True False Never Curl False GHCupURL True GPGNone False
let leanAppState = LeanAppState let leanAppState = LeanAppState
settings settings
dirs' dirs'
@@ -474,150 +353,6 @@ versionCompleter criteria tool = listIOCompleter $ do
return $ T.unpack . prettyVer . lVer <$> installedVersions return $ T.unpack . prettyVer . lVer <$> installedVersions
toolDlCompleter :: Tool -> Completer
toolDlCompleter tool = mkCompleter $ \case
"" -> pure (initUrl tool <> ["https://", "http://", "file:///"])
word
| "file://" `isPrefixOf` word -> fileUri' [] word
-- downloads.haskell.org
| "https://downloads.haskell.org/" `isPrefixOf` word ->
fmap (completePrefix word) . prefixMatch (FP.takeFileName word) <$> fromHRef word
-- github releases
| "https://github.com/haskell/haskell-language-server/releases/download/" `isPrefixOf` word
, let xs = splitPath word
, (length xs == 6 && last word == '/') || (length xs == 7 && last word /= '/') ->
fmap (\x -> completePrefix word x <> "/") . prefixMatch (FP.takeFileName word) <$> getGithubReleases "haskell" "haskell-language-server"
| "https://github.com/commercialhaskell/stack/releases/download/" == word
, let xs = splitPath word
, (length xs == 6 && last word == '/') || (length xs == 7 && last word /= '/') ->
fmap (\x -> completePrefix word x <> "/") . prefixMatch (FP.takeFileName word) <$> getGithubReleases "commercialhaskell" "stack"
-- github release assets
| "https://github.com/haskell/haskell-language-server/releases/download/" `isPrefixOf` word
, let xs = splitPath word
, (length xs == 7 && last word == '/') || length xs == 8
, let rel = xs !! 6
, length rel > 1 -> do
fmap (completePrefix word) . prefixMatch (FP.takeFileName word) <$> getGithubAssets "haskell" "haskell-language-server" (init rel)
| "https://github.com/commercialhaskell/stack/releases/download/" `isPrefixOf` word
, let xs = splitPath word
, (length xs == 7 && last word == '/') || length xs == 8
, let rel = xs !! 6
, length rel > 1 -> do
fmap (completePrefix word) . prefixMatch (FP.takeFileName word) <$> getGithubAssets "commercialhaskell" "stack" (init rel)
-- github
| "https://github.com/c" `isPrefixOf` word -> pure ["https://github.com/commercialhaskell/stack/releases/download/"]
| "https://github.com/h" `isPrefixOf` word -> pure ["https://github.com/haskell/haskell-language-server/releases/download/"]
| "https://g" `isPrefixOf` word
, tool == Stack -> pure ["https://github.com/commercialhaskell/stack/releases/download/"]
| "https://g" `isPrefixOf` word
, tool == HLS -> pure ["https://github.com/haskell/haskell-language-server/releases/download/"]
| "https://d" `isPrefixOf` word -> pure $ filter ("https://downloads.haskell.org/" `isPrefixOf`) $ initUrl tool
| "h" `isPrefixOf` word -> pure $ initUrl tool
| word `isPrefixOf` "file:///" -> pure ["file:///"]
| word `isPrefixOf` "https://" -> pure ["https://"]
| word `isPrefixOf` "http://" -> pure ["http://"]
| otherwise -> pure []
where
initUrl :: Tool -> [String]
initUrl GHC = [ "https://downloads.haskell.org/~ghc/"
, "https://downloads.haskell.org/~ghcup/unofficial-bindists/ghc/"
]
initUrl Cabal = [ "https://downloads.haskell.org/~cabal/"
, "https://downloads.haskell.org/~ghcup/unofficial-bindists/cabal/"
]
initUrl GHCup = [ "https://downloads.haskell.org/~ghcup/" ]
initUrl HLS = [ "https://github.com/haskell/haskell-language-server/releases/download/"
, "https://downloads.haskell.org/~ghcup/unofficial-bindists/haskell-language-server/"
]
initUrl Stack = [ "https://github.com/commercialhaskell/stack/releases/download/"
, "https://downloads.haskell.org/~ghcup/unofficial-bindists/stack/"
]
completePrefix :: String -- ^ url, e.g. 'https://github.com/haskell/haskell-languag'
-> String -- ^ match, e.g. 'haskell-language-server'
-> String -- ^ result, e.g. 'https://github.com/haskell/haskell-language-server'
completePrefix url match =
let base = FP.takeDirectory url
fn = FP.takeFileName url
in if fn `isPrefixOf` match then base <> "/" <> match else url
prefixMatch :: String -> [String] -> [String]
prefixMatch pref = filter (pref `isPrefixOf`)
fromHRef :: String -> IO [String]
fromHRef url = withCurl (FP.takeDirectory url) 2_000_000 $ \stdout ->
pure
. fmap (T.unpack . decUTF8Safe' . fromAttrib "href")
. filter isTagOpen
. filter (~== ("<a href>" :: String))
. parseTags
$ stdout
withCurl :: String -- ^ url
-> Int -- ^ delay
-> (ByteString -> IO [String]) -- ^ callback
-> IO [String]
withCurl url delay cb = do
let limit = threadDelay delay
race limit (executeOut "curl" ["-fL", url] Nothing) >>= \case
Right (CapturedProcess {_exitCode, _stdOut}) -> do
case _exitCode of
ExitSuccess ->
(try @_ @SomeException . cb $ _stdOut) >>= \case
Left _ -> pure []
Right r' -> do
r <- try @_ @SomeException
. evaluate
. force
$ r'
either (\_ -> pure []) pure r
ExitFailure _ -> pure []
Left _ -> pure []
getGithubReleases :: String
-> String
-> IO [String]
getGithubReleases owner repo = withCurl url 3_000_000 $ \stdout -> do
Just xs <- pure $ decode' @Array stdout
fmap V.toList $ forM xs $ \x -> do
(Object r) <- pure x
Just (String name) <- pure $ KM.lookup (mkval "tag_name") r
pure $ T.unpack name
where
url = "https://api.github.com/repos/" <> owner <> "/" <> repo <> "/releases"
getGithubAssets :: String
-> String
-> String
-> IO [String]
getGithubAssets owner repo tag = withCurl url 3_000_000 $ \stdout -> do
Just xs <- pure $ decode' @Object stdout
Just (Array assets) <- pure $ KM.lookup (mkval "assets") xs
as <- fmap V.toList $ forM assets $ \val -> do
(Object asset) <- pure val
Just (String name) <- pure $ KM.lookup (mkval "name") asset
pure $ T.unpack name
pure as
where
url = "https://api.github.com/repos/" <> owner <> "/" <> repo <> "/releases/tags/" <> tag
#if MIN_VERSION_aeson(2,0,0)
mkval = KM.fromString
#else
mkval = id
#endif
----------------- -----------------
@@ -661,7 +396,7 @@ fromVersion' :: ( HasLog env
] m (GHCTargetVersion, Maybe VersionInfo) ] m (GHCTargetVersion, Maybe VersionInfo)
fromVersion' SetRecommended tool = do fromVersion' SetRecommended tool = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
bimap mkTVer Just <$> getRecommended dls tool (\(x, y) -> (mkTVer x, Just y)) <$> getRecommended dls tool
?? TagNotFound Recommended tool ?? TagNotFound Recommended tool
fromVersion' (SetToolVersion v) tool = do fromVersion' (SetToolVersion v) tool = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
@@ -671,19 +406,19 @@ fromVersion' (SetToolVersion v) tool = do
Right pvpIn -> Right pvpIn ->
lift (getLatestToolFor tool pvpIn dls) >>= \case lift (getLatestToolFor tool pvpIn dls) >>= \case
Just (pvp_, vi') -> do Just (pvp_, vi') -> do
v' <- lift $ pvpToVersion pvp_ "" v' <- lift $ pvpToVersion pvp_
when (v' /= _tvVersion v) $ lift $ logWarn ("Assuming you meant version " <> prettyVer v') when (v' /= (_tvVersion v)) $ lift $ logWarn ("Assuming you meant version " <> prettyVer v')
pure (GHCTargetVersion (_tvTarget v) v', Just vi') pure (GHCTargetVersion (_tvTarget v) v', Just vi')
Nothing -> pure (v, vi) Nothing -> pure (v, vi)
fromVersion' (SetToolTag Latest) tool = do fromVersion' (SetToolTag Latest) tool = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
bimap mkTVer Just <$> getLatest dls tool ?? TagNotFound Latest tool (\(x, y) -> (mkTVer x, Just y)) <$> getLatest dls tool ?? TagNotFound Latest tool
fromVersion' (SetToolTag Recommended) tool = do fromVersion' (SetToolTag Recommended) tool = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
bimap mkTVer Just <$> getRecommended dls tool ?? TagNotFound Recommended tool (\(x, y) -> (mkTVer x, Just y)) <$> getRecommended dls tool ?? TagNotFound Recommended tool
fromVersion' (SetToolTag (Base pvp'')) GHC = do fromVersion' (SetToolTag (Base pvp'')) GHC = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
bimap mkTVer Just <$> getLatestBaseVersion dls pvp'' ?? TagNotFound (Base pvp'') GHC (\(x, y) -> (mkTVer x, Just y)) <$> getLatestBaseVersion dls pvp'' ?? TagNotFound (Base pvp'') GHC
fromVersion' SetNext tool = do fromVersion' SetNext tool = do
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
next <- case tool of next <- case tool of
@@ -695,7 +430,7 @@ fromVersion' SetNext tool = do
. dropWhile (\GHCTargetVersion {..} -> _tvVersion /= set) . dropWhile (\GHCTargetVersion {..} -> _tvVersion /= set)
. cycle . cycle
. sortBy (\x y -> compare (_tvVersion x) (_tvVersion y)) . sortBy (\x y -> compare (_tvVersion x) (_tvVersion y))
. filter (\GHCTargetVersion {..} -> isNothing _tvTarget) . filter (\GHCTargetVersion {..} -> _tvTarget == Nothing)
$ ghcs) ?? NoToolVersionSet tool $ ghcs) ?? NoToolVersionSet tool
Cabal -> do Cabal -> do
set <- cabalSet !? NoToolVersionSet tool set <- cabalSet !? NoToolVersionSet tool
@@ -744,22 +479,42 @@ checkForUpdates :: ( MonadReader env m
, MonadIO m , MonadIO m
, MonadFail m , MonadFail m
) )
=> m [(Tool, Version)] => m ()
checkForUpdates = do checkForUpdates = do
GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- getGHCupInfo
lInstalled <- listVersions Nothing (Just ListInstalled) lInstalled <- listVersions Nothing (Just ListInstalled)
let latestInstalled tool = (fmap lVer . lastMay . filter (\lr -> lTool lr == tool)) lInstalled let latestInstalled tool = (fmap lVer . lastMay . filter (\lr -> lTool lr == tool)) lInstalled
ghcup <- forMM (getLatest dls GHCup) $ \(l, _) -> do forM_ (getLatest dls GHCup) $ \(l, _) -> do
(Right ghcup_ver) <- pure $ version $ prettyPVP ghcUpVer (Right ghc_ver) <- pure $ version $ prettyPVP ghcUpVer
if (l > ghcup_ver) then pure $ Just (GHCup, l) else pure Nothing when (l > ghc_ver)
$ logWarn $
"New GHCup version available: " <> prettyVer l <> ". To upgrade, run 'ghcup upgrade'"
otherTools <- forM [GHC, Cabal, HLS, Stack] $ \t -> forM_ (getLatest dls GHC) $ \(l, _) -> do
forMM (getLatest dls t) $ \(l, _) -> do let mghc_ver = latestInstalled GHC
let mver = latestInstalled t forM mghc_ver $ \ghc_ver ->
forMM mver $ \ver -> when (l > ghc_ver)
if (l > ver) then pure $ Just (t, l) else pure Nothing $ logWarn $
"New GHC version available: " <> prettyVer l <> ". To upgrade, run 'ghcup install ghc " <> prettyVer l <> "'"
pure $ catMaybes (ghcup:otherTools) forM_ (getLatest dls Cabal) $ \(l, _) -> do
where let mcabal_ver = latestInstalled Cabal
forMM a f = fmap join $ forM a f forM mcabal_ver $ \cabal_ver ->
when (l > cabal_ver)
$ logWarn $
"New Cabal version available: " <> prettyVer l <> ". To upgrade, run 'ghcup install cabal " <> prettyVer l <> "'"
forM_ (getLatest dls HLS) $ \(l, _) -> do
let mhls_ver = latestInstalled HLS
forM mhls_ver $ \hls_ver ->
when (l > hls_ver)
$ logWarn $
"New HLS version available: " <> prettyVer l <> ". To upgrade, run 'ghcup install hls " <> prettyVer l <> "'"
forM_ (getLatest dls Stack) $ \(l, _) -> do
let mstack_ver = latestInstalled Stack
forM mstack_ver $ \stack_ver ->
when (l > stack_ver)
$ logWarn $
"New Stack version available: " <> prettyVer l <> ". To upgrade, run 'ghcup install stack " <> prettyVer l <> "'"

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -40,7 +41,6 @@ import Prelude hiding ( appendFile )
import System.Exit import System.Exit
import Text.PrettyPrint.HughesPJClass ( prettyShow ) import Text.PrettyPrint.HughesPJClass ( prettyShow )
import URI.ByteString hiding ( uriParser )
import qualified Data.Text as T import qualified Data.Text as T
import Control.Exception.Safe (MonadMask) import Control.Exception.Safe (MonadMask)
import System.FilePath (isPathSeparator) import System.FilePath (isPathSeparator)
@@ -69,7 +69,7 @@ data GHCCompileOptions = GHCCompileOptions
, bootstrapGhc :: Either Version FilePath , bootstrapGhc :: Either Version FilePath
, jobs :: Maybe Int , jobs :: Maybe Int
, buildConfig :: Maybe FilePath , buildConfig :: Maybe FilePath
, patches :: Maybe (Either FilePath [URI]) , patchDir :: Maybe FilePath
, crossTarget :: Maybe Text , crossTarget :: Maybe Text
, addConfArgs :: [Text] , addConfArgs :: [Text]
, setCompile :: Bool , setCompile :: Bool
@@ -85,11 +85,10 @@ data HLSCompileOptions = HLSCompileOptions
, setCompile :: Bool , setCompile :: Bool
, ovewrwiteVer :: Maybe Version , ovewrwiteVer :: Maybe Version
, isolateDir :: Maybe FilePath , isolateDir :: Maybe FilePath
, cabalProject :: Maybe (Either FilePath URI) , cabalProject :: Maybe FilePath
, cabalProjectLocal :: Maybe URI , cabalProjectLocal :: Maybe FilePath
, patches :: Maybe (Either FilePath [URI]) , patchDir :: Maybe FilePath
, targetGHCs :: [ToolVersion] , targetGHCs :: [ToolVersion]
, cabalArgs :: [Text]
} }
@@ -150,10 +149,7 @@ Examples:
These need to be available in PATH prior to compilation. These need to be available in PATH prior to compilation.
Examples: Examples:
# compile 1.4.0 for ghc 8.10.5 and 8.10.7 ghcup compile hls -v 1.4.0 -j 12 8.10.5 8.10.7 9.0.1|]
ghcup compile hls -v 1.4.0 -j 12 --ghc 8.10.5 --ghc 8.10.7
# compile from master for ghc 8.10.7, linking everything dynamically
ghcup compile hls -g master -j 12 --ghc 8.10.7 -- --ghc-options='-dynamic'|]
ghcCompileOpts :: Parser GHCCompileOptions ghcCompileOpts :: Parser GHCCompileOptions
@@ -165,7 +161,6 @@ ghcCompileOpts =
) )
(short 'v' <> long "version" <> metavar "VERSION" <> help (short 'v' <> long "version" <> metavar "VERSION" <> help
"The tool version to compile" "The tool version to compile"
<> (completer $ versionCompleter Nothing GHC)
) )
) <|> ) <|>
(Right <$> (GitBranch <$> option (Right <$> (GitBranch <$> option
@@ -173,10 +168,7 @@ ghcCompileOpts =
(short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help (short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help
"The git commit/branch/ref to build from" "The git commit/branch/ref to build from"
) <*> ) <*>
optional (option str ( optional (option str (short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to GHC upstream)"))
short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to GHC upstream)"
<> completer (gitFileUri ["https://gitlab.haskell.org/ghc/ghc.git"])
))
))) )))
<*> option <*> option
(eitherReader (eitherReader
@@ -189,14 +181,12 @@ ghcCompileOpts =
<> metavar "BOOTSTRAP_GHC" <> metavar "BOOTSTRAP_GHC"
<> help <> help
"The GHC version (or full path) to bootstrap with (must be installed)" "The GHC version (or full path) to bootstrap with (must be installed)"
<> (completer $ versionCompleter Nothing GHC)
) )
<*> optional <*> optional
(option (option
(eitherReader (readEither @Int)) (eitherReader (readEither @Int))
(short 'j' <> long "jobs" <> metavar "JOBS" <> help (short 'j' <> long "jobs" <> metavar "JOBS" <> help
"How many jobs to use for make" "How many jobs to use for make"
<> (completer $ listCompleter $ fmap show ([1..12] :: [Int]))
) )
) )
<*> optional <*> optional
@@ -204,28 +194,15 @@ ghcCompileOpts =
str str
(short 'c' <> long "config" <> metavar "CONFIG" <> help (short 'c' <> long "config" <> metavar "CONFIG" <> help
"Absolute path to build config file" "Absolute path to build config file"
<> completer (bashCompleter "file")
) )
) )
<*> (optional <*> optional
( (option
(fmap Right $ many $ option str
(eitherReader uriParser) (short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
(long "patch" <> metavar "PATCH_URI" <> help "Absolute path to patch directory (applies all .patch and .diff files in order using -p1)"
"URI to a patch (https/http/file)"
<> completer fileUri
)
)
<|>
(fmap Left $ option
str
(short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
"Absolute path to patch directory (applies all .patch and .diff files in order using -p1. This order is determined by a quilt series file if it exists, or the patches are lexicographically ordered)"
<> completer (bashCompleter "directory")
)
) )
) )
)
<*> optional <*> optional
(option (option
str str
@@ -234,7 +211,12 @@ ghcCompileOpts =
) )
) )
<*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to configure, prefix with '-- ' (longopts)")) <*> many (argument str (metavar "CONFIGURE_ARGS" <> help "Additional arguments to configure, prefix with '-- ' (longopts)"))
<*> fmap (fromMaybe False) (invertableSwitch "set" Nothing False (help "Set as active version after install")) <*> flag
False
True
(long "set" <> help
"Set as active version after install"
)
<*> optional <*> optional
(option (option
(eitherReader (eitherReader
@@ -242,7 +224,6 @@ ghcCompileOpts =
) )
(short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help (short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help
"Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'" "Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'"
<> (completer $ versionCompleter Nothing GHC)
) )
) )
<*> optional <*> optional
@@ -262,7 +243,6 @@ ghcCompileOpts =
<> long "isolate" <> long "isolate"
<> metavar "DIR" <> metavar "DIR"
<> help "install in an isolated directory instead of the default one, no symlinks to this installation will be made" <> help "install in an isolated directory instead of the default one, no symlinks to this installation will be made"
<> completer (bashCompleter "directory")
) )
) )
@@ -275,7 +255,6 @@ hlsCompileOpts =
) )
(short 'v' <> long "version" <> metavar "VERSION" <> help (short 'v' <> long "version" <> metavar "VERSION" <> help
"The tool version to compile" "The tool version to compile"
<> (completer $ versionCompleter Nothing HLS)
) )
) <|> ) <|>
(Right <$> (GitBranch <$> option (Right <$> (GitBranch <$> option
@@ -283,19 +262,21 @@ hlsCompileOpts =
(short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help (short 'g' <> long "git-ref" <> metavar "GIT_REFERENCE" <> help
"The git commit/branch/ref to build from" "The git commit/branch/ref to build from"
) <*> ) <*>
optional (option str (short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to HLS upstream)" optional (option str (short 'r' <> long "repository" <> metavar "GIT_REPOSITORY" <> help "The git repository to build from (defaults to GHC upstream)"))
<> completer (gitFileUri ["https://github.com/haskell/haskell-language-server.git"])
))
))) )))
<*> optional <*> optional
(option (option
(eitherReader (readEither @Int)) (eitherReader (readEither @Int))
(short 'j' <> long "jobs" <> metavar "JOBS" <> help (short 'j' <> long "jobs" <> metavar "JOBS" <> help
"How many jobs to use for make" "How many jobs to use for make"
<> (completer $ listCompleter $ fmap show ([1..12] :: [Int]))
) )
) )
<*> fmap (fromMaybe True) (invertableSwitch "set" Nothing True (help "Don't set as active version after install")) <*> flag
False
True
(long "set" <> help
"Set as active version after install"
)
<*> optional <*> optional
(option (option
(eitherReader (eitherReader
@@ -303,7 +284,6 @@ hlsCompileOpts =
) )
(short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help (short 'o' <> long "overwrite-version" <> metavar "OVERWRITE_VERSION" <> help
"Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'" "Allows to overwrite the finally installed VERSION with a different one, e.g. when you build 8.10.4 with your own patches, you might want to set this to '8.10.4-p1'"
<> (completer $ versionCompleter Nothing HLS)
) )
) )
<*> optional <*> optional
@@ -313,51 +293,30 @@ hlsCompileOpts =
<> long "isolate" <> long "isolate"
<> metavar "DIR" <> metavar "DIR"
<> help "install in an isolated directory instead of the default one, no symlinks to this installation will be made" <> help "install in an isolated directory instead of the default one, no symlinks to this installation will be made"
<> completer (bashCompleter "directory")
) )
) )
<*> optional <*> optional
(option (option
((fmap Right $ eitherReader uriParser) <|> (fmap Left str)) str
(long "cabal-project" <> metavar "CABAL_PROJECT" <> help (long "cabal-project" <> metavar "CABAL_PROJECT" <> help
"If relative filepath, specifies the path to cabal.project inside the unpacked HLS tarball/checkout. Otherwise expects a full URI with https/http/file scheme." "If relative, specifies the path to cabal.project inside the unpacked HLS tarball/checkout. If absolute, will copy the file over."
<> completer fileUri
) )
) )
<*> optional <*> optional
(option (option
(eitherReader uriParser) (eitherReader absolutePathParser)
(long "cabal-project-local" <> metavar "CABAL_PROJECT_LOCAL" <> help (long "cabal-project-local" <> metavar "CABAL_PROJECT_LOCAL" <> help
"URI (https/http/file) to a cabal.project.local to be used for the build. Will be copied over." "Absolute path to a cabal.project.local to be used for the build. Will be copied over."
<> completer fileUri
) )
) )
<*> (optional <*> optional
( (option
(fmap Right $ many $ option (eitherReader absolutePathParser)
(eitherReader uriParser) (short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
(long "patch" <> metavar "PATCH_URI" <> help "Absolute path to patch directory (applies all .patch and .diff files in order using -p1)"
"URI to a patch (https/http/file)"
<> completer fileUri
)
)
<|>
(fmap Left $ option
str
(short 'p' <> long "patchdir" <> metavar "PATCH_DIR" <> help
"Absolute path to patch directory (applies all .patch and .diff files in order using -p1)"
<> completer (bashCompleter "directory")
)
) )
) )
) <*> some (toolVersionArgument Nothing (Just GHC))
<*> some (
option (eitherReader toolVersionEither)
( long "ghc" <> metavar "GHC_VERSION|TAG" <> help "For which GHC version to compile for (can be specified multiple times)"
<> completer (tagCompleter GHC [])
<> completer (versionCompleter Nothing GHC))
)
<*> many (argument str (metavar "CABAL_ARGS" <> help "Additional arguments to cabal install, prefix with '-- ' (longopts)"))
@@ -388,7 +347,6 @@ type GHCEffects = '[ AlreadyInstalled
, ProcessError , ProcessError
, CopyError , CopyError
, BuildFailed , BuildFailed
, UninstallFailed
] ]
type HLSEffects = '[ AlreadyInstalled type HLSEffects = '[ AlreadyInstalled
, BuildFailed , BuildFailed
@@ -407,7 +365,6 @@ type HLSEffects = '[ AlreadyInstalled
, NotInstalled , NotInstalled
, DirNotEmpty , DirNotEmpty
, ArchiveResult , ArchiveResult
, UninstallFailed
] ]
@@ -447,11 +404,11 @@ compile :: ( Monad m
) )
=> CompileCommand => CompileCommand
-> Settings -> Settings
-> Dirs
-> (forall eff a . ReaderT AppState m (VEither eff a) -> m (VEither eff a)) -> (forall eff a . ReaderT AppState m (VEither eff a) -> m (VEither eff a))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
compile compileCommand settings Dirs{..} runAppState runLogger = do compile compileCommand settings runAppState runLogger = do
VRight Dirs{ .. } <- runAppState (VRight <$> getDirs)
case compileCommand of case compileCommand of
(CompileHLS HLSCompileOptions { .. }) -> do (CompileHLS HLSCompileOptions { .. }) -> do
runCompileHLS runAppState (do runCompileHLS runAppState (do
@@ -471,15 +428,14 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
ghcs ghcs
jobs jobs
ovewrwiteVer ovewrwiteVer
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
cabalProject cabalProject
cabalProjectLocal cabalProjectLocal
patches patchDir
cabalArgs
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
let vi = getVersionInfo targetVer HLS dls let vi = getVersionInfo targetVer HLS dls
when setCompile $ void $ liftE $ when setCompile $ void $ liftE $
setHLS targetVer SetHLSOnly Nothing setHLS targetVer
pure (vi, targetVer) pure (vi, targetVer)
) )
>>= \case >>= \case
@@ -494,7 +450,7 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
case keepDirs settings of case keepDirs settings of
Never -> runLogger $ logError $ T.pack $ prettyShow err Never -> runLogger $ logError $ T.pack $ prettyShow err
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <> _ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " "Check the logs at " <> T.pack logsDir <> " and the build directory "
<> T.pack tmpdir <> " for more clues." <> "\n" <> <> T.pack tmpdir <> " for more clues." <> "\n" <>
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.") "Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
pure $ ExitFailure 9 pure $ ExitFailure 9
@@ -522,15 +478,15 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
bootstrapGhc bootstrapGhc
jobs jobs
buildConfig buildConfig
patches patchDir
addConfArgs addConfArgs
buildFlavour buildFlavour
hadrian hadrian
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
let vi = getVersionInfo (_tvVersion targetVer) GHC dls let vi = getVersionInfo (_tvVersion targetVer) GHC dls
when setCompile $ void $ liftE $ when setCompile $ void $ liftE $
setGHC targetVer SetGHCOnly Nothing setGHC targetVer SetGHCOnly
pure (vi, targetVer) pure (vi, targetVer)
) )
>>= \case >>= \case
@@ -543,17 +499,17 @@ compile compileCommand settings Dirs{..} runAppState runLogger = do
pure ExitSuccess pure ExitSuccess
VLeft (V (AlreadyInstalled _ v)) -> do VLeft (V (AlreadyInstalled _ v)) -> do
runLogger $ logWarn $ runLogger $ logWarn $
"GHC ver " <> prettyVer v <> " already installed, remove it first to reinstall" "GHC ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install ghc --force " <> prettyVer v <> "'"
pure ExitSuccess pure ExitSuccess
VLeft (V (DirNotEmpty fp)) -> do VLeft (V (DirNotEmpty fp)) -> do
runLogger $ logError $ runLogger $ logWarn $
"Install directory " <> T.pack fp <> " is not empty." "Install directory " <> T.pack fp <> " is not empty. Use 'ghcup install ghc --isolate " <> T.pack fp <> " --force ..." <> "' to install regardless."
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft err@(V (BuildFailed tmpdir _)) -> do VLeft err@(V (BuildFailed tmpdir _)) -> do
case keepDirs settings of case keepDirs settings of
Never -> runLogger $ logError $ T.pack $ prettyShow err Never -> runLogger $ logError $ T.pack $ prettyShow err
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <> _ -> runLogger $ (logError $ T.pack (prettyShow err) <> "\n" <>
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " "Check the logs at " <> T.pack logsDir <> " and the build directory "
<> T.pack tmpdir <> " for more clues." <> "\n" <> <> T.pack tmpdir <> " for more clues." <> "\n" <>
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.") "Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
pure $ ExitFailure 9 pure $ ExitFailure 9

View File

@@ -2,12 +2,12 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ExplicitForAll #-}
module GHCup.OptParse.Config where module GHCup.OptParse.Config where
@@ -18,26 +18,23 @@ import GHCup.Utils
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.String.QQ import GHCup.Utils.String.QQ
import GHCup.OptParse.Common
#if !MIN_VERSION_base(4,13,0) #if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail ) import Control.Monad.Fail ( MonadFail )
#endif #endif
import Control.Exception ( displayException )
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bifunctor
import Data.Functor import Data.Functor
import Data.Maybe import Data.Maybe
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Options.Applicative hiding ( style, ParseError ) import Options.Applicative hiding ( style )
import Options.Applicative.Help.Pretty ( text )
import Prelude hiding ( appendFile ) import Prelude hiding ( appendFile )
import System.Exit import System.Exit
import URI.ByteString hiding ( uriParser )
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.ByteString.UTF8 as UTF8 import qualified Data.ByteString.UTF8 as UTF8
import qualified Data.Yaml.Aeson as Y import qualified Data.YAML.Aeson as Y
import Control.Exception.Safe (MonadMask) import Control.Exception.Safe (MonadMask)
@@ -50,9 +47,8 @@ import Control.Exception.Safe (MonadMask)
data ConfigCommand data ConfigCommand
= ShowConfig = ShowConfig
| SetConfig String (Maybe String) | SetConfig String String
| InitConfig | InitConfig
| AddReleaseChannel URI
@@ -66,17 +62,14 @@ configP = subparser
( command "init" initP ( command "init" initP
<> command "set" setP -- [set] KEY VALUE at help lhs <> command "set" setP -- [set] KEY VALUE at help lhs
<> command "show" showP <> command "show" showP
<> command "add-release-channel" addP
) )
<|> argsP -- add show for a single option <|> argsP -- add show for a single option
<|> pure ShowConfig <|> pure ShowConfig
where where
initP = info (pure InitConfig) (progDesc "Write default config to ~/.ghcup/config.yaml") initP = info (pure InitConfig) (progDesc "Write default config to ~/.ghcup/config.yaml")
showP = info (pure ShowConfig) (progDesc "Show current config (default)") showP = info (pure ShowConfig) (progDesc "Show current config (default)")
setP = info argsP (progDesc "Set config KEY to VALUE (or specify as single json value)" <> footerDoc (Just $ text configSetFooter)) setP = info argsP (progDesc "Set config KEY to VALUE")
argsP = SetConfig <$> argument str (metavar "<JSON_VALUE | YAML_KEY>") <*> optional (argument str (metavar "YAML_VALUE")) argsP = SetConfig <$> argument str (metavar "KEY") <*> argument str (metavar "VALUE")
addP = info (AddReleaseChannel <$> argument (eitherReader uriParser) (metavar "URI" <> completer fileUri))
(progDesc "Add a release channel from a URI")
@@ -96,19 +89,7 @@ configFooter = [s|Examples:
ghcup config init ghcup config init
# set <key> <value> configuration pair # set <key> <value> configuration pair
ghcup config set <key> <value>|] ghcup config <key> <value>|]
configSetFooter :: String
configSetFooter = [s|Examples:
# disable caching
ghcup config set cache false
# switch downloader to wget
ghcup config set downloader Wget
# set mirror for ghcup metadata
ghcup config set '{url-source: { OwnSource: "<url>"}}'|]
@@ -118,21 +99,25 @@ configSetFooter = [s|Examples:
formatConfig :: UserSettings -> String formatConfig :: UserSettings -> String
formatConfig = UTF8.toString . Y.encode formatConfig = UTF8.toString . Y.encode1Strict
updateSettings :: UserSettings -> Settings -> Settings updateSettings :: Monad m => UTF8.ByteString -> Settings -> Excepts '[JSONError] m Settings
updateSettings UserSettings{..} Settings{..} = updateSettings config' settings = do
let cache' = fromMaybe cache uCache settings' <- lE' JSONDecodeError . first snd . Y.decode1Strict $ config'
metaCache' = fromMaybe metaCache uMetaCache pure $ mergeConf settings' settings
noVerify' = fromMaybe noVerify uNoVerify where
keepDirs' = fromMaybe keepDirs uKeepDirs mergeConf :: UserSettings -> Settings -> Settings
downloader' = fromMaybe downloader uDownloader mergeConf UserSettings{..} Settings{..} =
verbose' = fromMaybe verbose uVerbose let cache' = fromMaybe cache uCache
urlSource' = fromMaybe urlSource uUrlSource noVerify' = fromMaybe noVerify uNoVerify
noNetwork' = fromMaybe noNetwork uNoNetwork keepDirs' = fromMaybe keepDirs uKeepDirs
gpgSetting' = fromMaybe gpgSetting uGPGSetting downloader' = fromMaybe downloader uDownloader
in Settings cache' metaCache' noVerify' keepDirs' downloader' verbose' urlSource' noNetwork' gpgSetting' noColor verbose' = fromMaybe verbose uVerbose
urlSource' = fromMaybe urlSource uUrlSource
noNetwork' = fromMaybe noNetwork uNoNetwork
gpgSetting' = fromMaybe gpgSetting uGPGSetting
in Settings cache' noVerify' keepDirs' downloader' verbose' urlSource' noNetwork' gpgSetting' noColor
@@ -142,7 +127,7 @@ updateSettings UserSettings{..} Settings{..} =
config :: forall m. ( Monad m config :: ( Monad m
, MonadMask m , MonadMask m
, MonadUnliftIO m , MonadUnliftIO m
, MonadFail m , MonadFail m
@@ -163,42 +148,22 @@ config configCommand settings keybindings runLogger = case configCommand of
liftIO $ putStrLn $ formatConfig $ fromSettings settings (Just keybindings) liftIO $ putStrLn $ formatConfig $ fromSettings settings (Just keybindings)
pure ExitSuccess pure ExitSuccess
(SetConfig k mv) -> do (SetConfig k v) -> do
r <- runE @'[JSONError, ParseError] $ do case v of
case mv of "" -> do
Just "" -> runLogger $ logError "Empty values are not allowed"
throwE $ ParseError "Empty values are not allowed" pure $ ExitFailure 55
Nothing -> do _ -> do
usersettings <- decodeSettings k r <- runE @'[JSONError] $ do
lift $ doConfig usersettings settings' <- updateSettings (UTF8.fromString (k <> ": " <> v <> "\n")) settings
path <- liftIO getConfigFilePath
liftIO $ writeFile path $ formatConfig $ fromSettings settings' (Just keybindings)
lift $ runLogger $ logDebug $ T.pack $ show settings'
pure () pure ()
Just v -> do
usersettings <- decodeSettings (k <> ": " <> v <> "\n")
lift $ doConfig usersettings
pure ()
case r of
VRight _ -> pure ExitSuccess
VLeft (V (JSONDecodeError e)) -> do
runLogger $ logError $ "Error decoding config: " <> T.pack e
pure $ ExitFailure 65
VLeft _ -> pure $ ExitFailure 65
AddReleaseChannel uri -> do case r of
case urlSource settings of VRight _ -> pure ExitSuccess
AddSource xs -> do VLeft (V (JSONDecodeError e)) -> do
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource (xs <> [Right uri]) }) runLogger $ logError $ "Error decoding config: " <> T.pack e
pure ExitSuccess pure $ ExitFailure 65
_ -> do VLeft _ -> pure $ ExitFailure 65
doConfig (defaultUserSettings { uUrlSource = Just $ AddSource [Right uri] })
pure ExitSuccess
where
doConfig :: MonadIO m => UserSettings -> m ()
doConfig usersettings = do
let settings' = updateSettings usersettings settings
path <- liftIO getConfigFilePath
liftIO $ writeFile path $ formatConfig $ fromSettings settings' (Just keybindings)
runLogger $ logDebug $ T.pack $ show settings'
pure ()
decodeSettings = lE' (JSONDecodeError . displayException) . Y.decodeEither' . UTF8.fromString

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -51,7 +52,7 @@ describe_result = $( LitE . StringL <$>
runIO (do runIO (do
CapturedProcess{..} <- do CapturedProcess{..} <- do
dirs <- liftIO getAllDirs dirs <- liftIO getAllDirs
let settings = AppState (defaultSettings { noNetwork = True }) let settings = AppState (Settings True False Never Curl False GHCupURL False GPGNone False)
dirs dirs
defaultKeyBindings defaultKeyBindings
flip runReaderT settings $ executeOut "git" ["describe"] Nothing flip runReaderT settings $ executeOut "git" ["describe"] Nothing
@@ -83,7 +84,7 @@ prettyDebugInfo DebugInfo {..} = "Debug Info" <> "\n" <>
type DInfoEffects = '[ NoCompatiblePlatform , NoCompatibleArch , DistroNotFound ] type DInfoEffects = '[ NoCompatiblePlatform , NoCompatibleArch , DistroNotFound ]
runDebugInfo :: (ReaderT env m (VEither DInfoEffects a) -> m (VEither DInfoEffects a)) runDebugInfo :: (ReaderT env m (VEither DInfoEffects a) -> m (VEither DInfoEffects a))
-> Excepts DInfoEffects (ReaderT env m) a -> (Excepts DInfoEffects (ReaderT env m) a)
-> m (VEither DInfoEffects a) -> m (VEither DInfoEffects a)
runDebugInfo runAppState = runDebugInfo runAppState =
runAppState runAppState

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -98,12 +99,12 @@ gcFooter = [s|Discussion:
--------------------------- ---------------------------
type GCEffects = '[ NotInstalled, UninstallFailed ] type GCEffects = '[ NotInstalled ]
runGC :: MonadUnliftIO m runGC :: MonadUnliftIO m
=> (ReaderT AppState m (VEither GCEffects a) -> m (VEither GCEffects a)) => (ReaderT AppState m (VEither GCEffects a) -> m (VEither GCEffects a))
-> Excepts GCEffects (ResourceT (ReaderT AppState m)) a -> (Excepts GCEffects (ResourceT (ReaderT AppState m)) a)
-> m (VEither GCEffects a) -> m (VEither GCEffects a)
runGC runAppState = runGC runAppState =
runAppState runAppState
@@ -129,10 +130,10 @@ gc :: ( Monad m
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
gc GCOptions{..} runAppState runLogger = runGC runAppState (do gc GCOptions{..} runAppState runLogger = runGC runAppState (do
when gcOldGHC (liftE rmOldGHC) when gcOldGHC rmOldGHC
lift $ when gcProfilingLibs rmProfilingLibs lift $ when gcProfilingLibs rmProfilingLibs
lift $ when gcShareDir rmShareDir lift $ when gcShareDir rmShareDir
liftE $ when gcHLSNoGHC rmHLSNoGHC lift $ when gcHLSNoGHC rmHLSNoGHC
lift $ when gcCache rmCache lift $ when gcCache rmCache
lift $ when gcTmp rmTmp lift $ when gcTmp rmTmp
) >>= \case ) >>= \case

View File

@@ -2,11 +2,11 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE ViewPatterns #-}
module GHCup.OptParse.Install where module GHCup.OptParse.Install where
@@ -18,7 +18,7 @@ import GHCup.OptParse.Common
import GHCup import GHCup
import GHCup.Errors import GHCup.Errors
import GHCup.Types import GHCup.Types
import GHCup.Utils.Dirs import GHCup.Utils.File
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.String.QQ import GHCup.Utils.String.QQ
@@ -38,7 +38,7 @@ import Options.Applicative.Help.Pretty ( text )
import Prelude hiding ( appendFile ) import Prelude hiding ( appendFile )
import System.Exit import System.Exit
import Text.PrettyPrint.HughesPJClass ( prettyShow ) import Text.PrettyPrint.HughesPJClass ( prettyShow )
import URI.ByteString hiding ( uriParser ) import URI.ByteString
import qualified Data.Text as T import qualified Data.Text as T
@@ -188,18 +188,21 @@ installOpts tool =
<*> ( ( (,) <*> ( ( (,)
<$> optional <$> optional
(option (option
(eitherReader uriParser) (eitherReader bindistParser)
(short 'u' <> long "url" <> metavar "BINDIST_URL" <> help (short 'u' <> long "url" <> metavar "BINDIST_URL" <> help
"Install the specified version from this bindist" "Install the specified version from this bindist"
<> completer (toolDlCompleter (fromMaybe GHC tool))
) )
) )
<*> (Just <$> toolVersionArgument Nothing tool) <*> (Just <$> toolVersionArgument Nothing tool)
) )
<|> pure (Nothing, Nothing) <|> pure (Nothing, Nothing)
) )
<*> fmap (fromMaybe setDefault) (invertableSwitch "set" Nothing setDefault <*> flag
(help $ if not setDefault then "Set as active version after install" else "Don't set as active version after install")) False
True
(long "set" <> help
"Set as active version after install"
)
<*> optional <*> optional
(option (option
(eitherReader isolateParser) (eitherReader isolateParser)
@@ -207,16 +210,10 @@ installOpts tool =
<> long "isolate" <> long "isolate"
<> metavar "DIR" <> metavar "DIR"
<> help "install in an isolated dir instead of the default one" <> help "install in an isolated dir instead of the default one"
<> completer (bashCompleter "directory")
) )
) )
<*> switch <*> switch
(short 'f' <> long "force" <> help "Force install (THIS IS UNSAFE, only use it in Dockerfiles or CI)") (short 'f' <> long "force" <> help "Force install")
where
setDefault = case tool of
Nothing -> False
Just GHC -> False
Just _ -> True
@@ -258,50 +255,6 @@ type InstallEffects = '[ AlreadyInstalled
, NoToolVersionSet , NoToolVersionSet
, FileAlreadyExistsError , FileAlreadyExistsError
, ProcessError , ProcessError
, UninstallFailed
, (AlreadyInstalled, ())
, (UnknownArchive, ())
, (ArchiveResult, ())
, (FileDoesNotExistError, ())
, (CopyError, ())
, (NotInstalled, ())
, (UninstallFailed, ())
, (DirNotEmpty, ())
, (NoDownload, ())
, (BuildFailed, ())
, (TagNotFound, ())
, (DigestError, ())
, (GPGError, ())
, (DownloadFailed, ())
, (TarDirDoesNotExist, ())
, (NextVerNotFound, ())
, (NoToolVersionSet, ())
, (FileAlreadyExistsError, ())
, (ProcessError, ())
, (AlreadyInstalled, NotInstalled)
, (UnknownArchive, NotInstalled)
, (ArchiveResult, NotInstalled)
, (FileDoesNotExistError, NotInstalled)
, (CopyError, NotInstalled)
, (NotInstalled, NotInstalled)
, (DirNotEmpty, NotInstalled)
, (NoDownload, NotInstalled)
, (NotInstalled, NotInstalled)
, (UninstallFailed, NotInstalled)
, (BuildFailed, NotInstalled)
, (TagNotFound, NotInstalled)
, (DigestError, NotInstalled)
, (GPGError, NotInstalled)
, (DownloadFailed, NotInstalled)
, (TarDirDoesNotExist, NotInstalled)
, (NextVerNotFound, NotInstalled)
, (NoToolVersionSet, NotInstalled)
, (FileAlreadyExistsError, NotInstalled)
, (ProcessError, NotInstalled)
, ((), NotInstalled)
] ]
@@ -316,74 +269,13 @@ runInstTool appstate' mInstPlatform =
@InstallEffects @InstallEffects
type InstallGHCEffects = '[ TagNotFound
, NextVerNotFound
, NoToolVersionSet
, BuildFailed
, DirNotEmpty
, AlreadyInstalled
, UninstallFailed
, (AlreadyInstalled, NotInstalled)
, (UnknownArchive, NotInstalled)
, (ArchiveResult, NotInstalled)
, (FileDoesNotExistError, NotInstalled)
, (CopyError, NotInstalled)
, (NotInstalled, NotInstalled)
, (DirNotEmpty, NotInstalled)
, (NoDownload, NotInstalled)
, (UninstallFailed, NotInstalled)
, (BuildFailed, NotInstalled)
, (TagNotFound, NotInstalled)
, (DigestError, NotInstalled)
, (GPGError, NotInstalled)
, (DownloadFailed, NotInstalled)
, (TarDirDoesNotExist, NotInstalled)
, (NextVerNotFound, NotInstalled)
, (NoToolVersionSet, NotInstalled)
, (FileAlreadyExistsError, NotInstalled)
, (ProcessError, NotInstalled)
, (AlreadyInstalled, ())
, (UnknownArchive, ())
, (ArchiveResult, ())
, (FileDoesNotExistError, ())
, (CopyError, ())
, (NotInstalled, ())
, (DirNotEmpty, ())
, (NoDownload, ())
, (UninstallFailed, ())
, (BuildFailed, ())
, (TagNotFound, ())
, (DigestError, ())
, (GPGError, ())
, (DownloadFailed, ())
, (TarDirDoesNotExist, ())
, (NextVerNotFound, ())
, (NoToolVersionSet, ())
, (FileAlreadyExistsError, ())
, (ProcessError, ())
, ((), NotInstalled)
]
runInstGHC :: AppState
-> Maybe PlatformRequest
-> Excepts InstallGHCEffects (ResourceT (ReaderT AppState IO)) a
-> IO (VEither InstallGHCEffects a)
runInstGHC appstate' mInstPlatform =
flip runReaderT (maybe appstate' (\x -> appstate'{ pfreq = x } :: AppState) mInstPlatform)
. runResourceT
. runE
@InstallGHCEffects
------------------- -------------------
--[ Entrypoints ]-- --[ Entrypoints ]--
------------------- -------------------
install :: Either InstallCommand InstallOptions -> Settings -> IO AppState -> (ReaderT LeanAppState IO () -> IO ()) -> IO ExitCode install :: (Either InstallCommand InstallOptions) -> Settings -> IO AppState -> (ReaderT LeanAppState IO () -> IO ()) -> IO ExitCode
install installCommand settings getAppState' runLogger = case installCommand of install installCommand settings getAppState' runLogger = case installCommand of
(Right iopts) -> do (Right iopts) -> do
runLogger (logWarn "This is an old-style command for installing GHC. Use 'ghcup install ghc' instead.") runLogger (logWarn "This is an old-style command for installing GHC. Use 'ghcup install ghc' instead.")
@@ -397,25 +289,23 @@ install installCommand settings getAppState' runLogger = case installCommand of
installGHC InstallOptions{..} = do installGHC InstallOptions{..} = do
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState' s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
(case instBindist of (case instBindist of
Nothing -> runInstGHC s' instPlatform $ do Nothing -> runInstTool s' instPlatform $ do
(v, vi) <- liftE $ fromVersion instVer GHC (v, vi) <- liftE $ fromVersion instVer GHC
void $ liftE $ sequenceE (installGHCBin liftE $ installGHCBin
(_tvVersion v) (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) when instSet $ void $ liftE $ setGHC v SetGHCOnly
$ when instSet $ when (isNothing isolateDir) $ void $ setGHC v SetGHCOnly Nothing
pure vi pure vi
Just uri -> do Just uri -> do
runInstGHC s'{ settings = settings {noVerify = True}} instPlatform $ do runInstTool s'{ settings = settings {noVerify = True}} instPlatform $ do
(v, vi) <- liftE $ fromVersion instVer GHC (v, vi) <- liftE $ fromVersion instVer GHC
void $ liftE $ sequenceE (installGHCBindist liftE $ installGHCBindist
(DownloadInfo uri (Just $ RegexDir "ghc-.*") "") (DownloadInfo uri (Just $ RegexDir "ghc-.*") "")
(_tvVersion v) (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) when instSet $ void $ liftE $ setGHC v SetGHCOnly
$ when instSet $ when (isNothing isolateDir) $ void $ setGHC v SetGHCOnly Nothing
pure vi pure vi
) )
>>= \case >>= \case
@@ -424,44 +314,25 @@ install installCommand settings getAppState' runLogger = case installCommand of
forM_ (_viPostInstall =<< vi) $ \msg -> forM_ (_viPostInstall =<< vi) $ \msg ->
runLogger $ logInfo msg runLogger $ logInfo msg
pure ExitSuccess pure ExitSuccess
VLeft (V (AlreadyInstalled _ v, ())) -> do
runLogger $ logWarn $
"GHC ver " <> prettyVer v <> " already installed, remove it first to reinstall"
pure ExitSuccess
VLeft (V (AlreadyInstalled _ v)) -> do VLeft (V (AlreadyInstalled _ v)) -> do
runLogger $ logWarn $ runLogger $ logWarn $
"GHC ver " <> prettyVer v <> " already installed, remove it first to reinstall" "GHC ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install ghc --force " <> prettyVer v <> "'"
pure ExitSuccess pure ExitSuccess
VLeft (V (DirNotEmpty fp)) -> do VLeft (V (DirNotEmpty fp)) -> do
runLogger $ logError $ runLogger $ logWarn $
"Install directory " <> T.pack fp <> " is not empty." "Install directory " <> T.pack fp <> " is not empty. Use 'ghcup install ghc --isolate " <> T.pack fp <> " --force ..." <> "' to install regardless."
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft (V (DirNotEmpty fp, ())) -> do
runLogger $ logError $
"Install directory " <> T.pack fp <> " is not empty."
pure $ ExitFailure 3
VLeft err@(V (BuildFailed tmpdir _)) -> do VLeft err@(V (BuildFailed tmpdir _)) -> do
case keepDirs settings of case keepDirs settings of
Never -> runLogger (logError $ T.pack $ prettyShow err) Never -> runLogger $ (logError $ T.pack $ prettyShow err)
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <> _ -> runLogger $ (logError $ T.pack (prettyShow err) <> "\n" <>
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <> "Check the logs at " <> T.pack logsDir <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.") "Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft err@(V (BuildFailed tmpdir _, ())) -> do
case keepDirs settings of
Never -> runLogger (logError $ T.pack $ prettyShow err)
_ -> runLogger (logError $ T.pack (prettyShow err) <> "\n" <>
"Check the logs at " <> T.pack (fromGHCupPath logsDir) <> " and the build directory " <> T.pack tmpdir <> " for more clues." <> "\n" <>
"Make sure to clean up " <> T.pack tmpdir <> " afterwards.")
pure $ ExitFailure 3
VLeft e -> do VLeft e -> do
runLogger $ do runLogger $ do
logError $ T.pack $ prettyShow e logError $ T.pack $ prettyShow e
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir) logError $ "Also check the logs in " <> T.pack logsDir
pure $ ExitFailure 3 pure $ ExitFailure 3
@@ -470,22 +341,20 @@ install installCommand settings getAppState' runLogger = case installCommand of
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState' s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
(case instBindist of (case instBindist of
Nothing -> runInstTool s' instPlatform $ do Nothing -> runInstTool s' instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Cabal (v, vi) <- liftE $ fromVersion instVer Cabal
void $ liftE $ sequenceE (installCabalBin liftE $ installCabalBin
v (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setCabal v
pure vi pure vi
Just uri -> do Just uri -> do
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Cabal (v, vi) <- liftE $ fromVersion instVer Cabal
void $ liftE $ sequenceE (installCabalBindist liftE $ installCabalBindist
(DownloadInfo uri Nothing "") (DownloadInfo uri Nothing "")
v (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setCabal v
pure vi pure vi
) )
>>= \case >>= \case
@@ -502,18 +371,10 @@ install installCommand settings getAppState' runLogger = case installCommand of
runLogger $ logWarn $ runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite." "File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft (V (AlreadyInstalled _ v, ())) -> do
runLogger $ logWarn $
"Cabal ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install cabal --force " <> prettyVer v <> "'"
pure ExitSuccess
VLeft (V (FileAlreadyExistsError fp, ())) -> do
runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install cabal --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3
VLeft e -> do VLeft e -> do
runLogger $ do runLogger $ do
logError $ T.pack $ prettyShow e logError $ T.pack $ prettyShow e
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir) logError $ "Also check the logs in " <> T.pack logsDir
pure $ ExitFailure 4 pure $ ExitFailure 4
installHLS :: InstallOptions -> IO ExitCode installHLS :: InstallOptions -> IO ExitCode
@@ -521,23 +382,20 @@ install installCommand settings getAppState' runLogger = case installCommand of
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState' s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
(case instBindist of (case instBindist of
Nothing -> runInstTool s' instPlatform $ do Nothing -> runInstTool s' instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer HLS (v, vi) <- liftE $ fromVersion instVer HLS
void $ liftE $ sequenceE (installHLSBin liftE $ installHLSBin
v (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setHLS v SetHLSOnly Nothing
pure vi pure vi
Just uri -> do Just uri -> do
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer HLS (v, vi) <- liftE $ fromVersion instVer HLS
-- TODO: support legacy liftE $ installHLSBindist
void $ liftE $ sequenceE (installHLSBindist (DownloadInfo uri Nothing "")
(DownloadInfo uri (Just $ RegexDir "haskell-language-server-*") "") (_tvVersion v)
v isolateDir
(maybe GHCupInternal IsolateDir isolateDir) forceInstall
forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setHLS v SetHLSOnly Nothing
pure vi pure vi
) )
>>= \case >>= \case
@@ -558,22 +416,10 @@ install installCommand settings getAppState' runLogger = case installCommand of
runLogger $ logWarn $ runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite." "File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft (V (AlreadyInstalled _ v, ())) -> do
runLogger $ logWarn $
"HLS ver "
<> prettyVer v
<> " already installed; if you really want to reinstall it, you may want to run 'ghcup install hls --force "
<> prettyVer v
<> "'"
pure ExitSuccess
VLeft (V (FileAlreadyExistsError fp, ())) -> do
runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install hls --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3
VLeft e -> do VLeft e -> do
runLogger $ do runLogger $ do
logError $ T.pack $ prettyShow e logError $ T.pack $ prettyShow e
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir) logError $ "Also check the logs in " <> T.pack logsDir
pure $ ExitFailure 4 pure $ ExitFailure 4
installStack :: InstallOptions -> IO ExitCode installStack :: InstallOptions -> IO ExitCode
@@ -581,22 +427,20 @@ install installCommand settings getAppState' runLogger = case installCommand of
s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState' s'@AppState{ dirs = Dirs{ .. } } <- liftIO getAppState'
(case instBindist of (case instBindist of
Nothing -> runInstTool s' instPlatform $ do Nothing -> runInstTool s' instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Stack (v, vi) <- liftE $ fromVersion instVer Stack
void $ liftE $ sequenceE (installStackBin liftE $ installStackBin
v (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setStack v
pure vi pure vi
Just uri -> do Just uri -> do
runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do runInstTool s'{ settings = settings { noVerify = True}} instPlatform $ do
(_tvVersion -> v, vi) <- liftE $ fromVersion instVer Stack (v, vi) <- liftE $ fromVersion instVer Stack
void $ liftE $ sequenceE (installStackBindist liftE $ installStackBindist
(DownloadInfo uri Nothing "") (DownloadInfo uri Nothing "")
v (_tvVersion v)
(maybe GHCupInternal IsolateDir isolateDir) isolateDir
forceInstall forceInstall
) $ when instSet $ when (isNothing isolateDir) $ void $ setStack v
pure vi pure vi
) )
>>= \case >>= \case
@@ -613,17 +457,9 @@ install installCommand settings getAppState' runLogger = case installCommand of
runLogger $ logWarn $ runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite." "File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3 pure $ ExitFailure 3
VLeft (V (AlreadyInstalled _ v, ())) -> do
runLogger $ logWarn $
"Stack ver " <> prettyVer v <> " already installed; if you really want to reinstall it, you may want to run 'ghcup install stack --force " <> prettyVer v <> "'"
pure ExitSuccess
VLeft (V (FileAlreadyExistsError fp, ())) -> do
runLogger $ logWarn $
"File " <> T.pack fp <> " already exists. Use 'ghcup install stack --isolate " <> T.pack fp <> " --force ..." <> "' if you want to overwrite."
pure $ ExitFailure 3
VLeft e -> do VLeft e -> do
runLogger $ do runLogger $ do
logError $ T.pack $ prettyShow e logError $ T.pack $ prettyShow e
logError $ "Also check the logs in " <> T.pack (fromGHCupPath logsDir) logError $ "Also check the logs in " <> T.pack logsDir
pure $ ExitFailure 4 pure $ ExitFailure 4

View File

@@ -1,7 +1,11 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -11,7 +15,6 @@ module GHCup.OptParse.List where
import GHCup import GHCup
import GHCup.Utils.Prelude
import GHCup.Types import GHCup.Types
import GHCup.OptParse.Common import GHCup.OptParse.Common
@@ -69,7 +72,6 @@ listOpts =
(eitherReader toolParser) (eitherReader toolParser)
(short 't' <> long "tool" <> metavar "<ghc|cabal|hls|stack>" <> help (short 't' <> long "tool" <> metavar "<ghc|cabal|hls|stack>" <> help
"Tool to list versions for. Default is all" "Tool to list versions for. Default is all"
<> completer (toolCompleter)
) )
) )
<*> optional <*> optional
@@ -79,7 +81,6 @@ listOpts =
<> long "show-criteria" <> long "show-criteria"
<> metavar "<installed|set|available>" <> metavar "<installed|set|available>"
<> help "Show only installed/set/available tool versions" <> help "Show only installed/set/available tool versions"
<> completer (listCompleter ["installed", "set", "available"])
) )
) )
<*> switch <*> switch
@@ -98,7 +99,7 @@ printListResult :: Bool -> Bool -> [ListResult] -> IO ()
printListResult no_color raw lr = do printListResult no_color raw lr = do
let let
color | raw || no_color = (\_ x -> x) color | raw || no_color = flip const
| otherwise = Pretty.color | otherwise = Pretty.color
let let
@@ -118,16 +119,22 @@ printListResult no_color raw lr = do
. fmap . fmap
(\ListResult {..} -> (\ListResult {..} ->
let marks = if let marks = if
| lSet -> (color Green (if isWindows then "IS" else "✔✔")) #if defined(IS_WINDOWS)
| lInstalled -> (color Green (if isWindows then "I " else "")) | lSet -> (color Green "IS")
| otherwise -> (color Red (if isWindows then "X " else "")) | lInstalled -> (color Green "I ")
| otherwise -> (color Red "X ")
#else
| lSet -> (color Green "✔✔")
| lInstalled -> (color Green "")
| otherwise -> (color Red "")
#endif
in in
(if raw then [] else [marks]) (if raw then [] else [marks])
++ [ fmap toLower . show $ lTool ++ [ fmap toLower . show $ lTool
, case lCross of , case lCross of
Nothing -> T.unpack . prettyVer $ lVer Nothing -> T.unpack . prettyVer $ lVer
Just c -> T.unpack (c <> "-" <> prettyVer lVer) Just c -> T.unpack (c <> "-" <> prettyVer lVer)
, intercalate "," (filter (/= "") . fmap printTag $ sort lTag) , intercalate "," $ (filter (/= "") . fmap printTag $ sort lTag)
, intercalate "," , intercalate ","
$ (if hlsPowered $ (if hlsPowered
then [color Green "hls-powered"] then [color Green "hls-powered"]
@@ -143,11 +150,11 @@ printListResult no_color raw lr = do
) )
$ lr $ lr
let cols = let cols =
foldr (\xs ys -> zipWith (:) xs ys) (repeat []) rows foldr (\xs ys -> zipWith (:) xs ys) (replicate (length rows) []) rows
lengths = fmap (maximum . fmap strWidth) cols lengths = fmap maximum . (fmap . fmap) strWidth $ cols
padded = fmap (\xs -> zipWith padTo xs lengths) rows padded = fmap (\xs -> zipWith padTo xs lengths) rows
forM_ (if raw then rows else padded) $ \row -> putStrLn $ unwords row forM_ padded $ \row -> putStrLn $ intercalate " " row
where where
padTo str' x = padTo str' x =

View File

@@ -3,6 +3,9 @@
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -42,11 +45,11 @@ import Control.Concurrent (threadDelay)
--------------------------- ---------------------------
type NukeEffects = '[ NotInstalled, UninstallFailed ] type NukeEffects = '[ NotInstalled ]
runNuke :: AppState runNuke :: AppState
-> Excepts NukeEffects (ReaderT AppState m) a -> (Excepts NukeEffects (ReaderT AppState m) a)
-> m (VEither NukeEffects a) -> m (VEither NukeEffects a)
runNuke s' = runNuke s' =
flip runReaderT s' . runE @NukeEffects flip runReaderT s' . runE @NukeEffects

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -83,8 +84,8 @@ prefetchP = subparser
(PrefetchGHC (PrefetchGHC
<$> (PrefetchGHCOptions <$> (PrefetchGHCOptions
<$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper ) <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
<*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))) <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)")))
<*> optional (toolVersionArgument Nothing (Just GHC)) ) <*> ( optional (toolVersionArgument Nothing (Just GHC)) ))
( progDesc "Download GHC assets for installation") ( progDesc "Download GHC assets for installation")
) )
<> <>
@@ -92,7 +93,7 @@ prefetchP = subparser
"cabal" "cabal"
(info (info
(PrefetchCabal (PrefetchCabal
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))) <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)")))
<*> ( optional (toolVersionArgument Nothing (Just Cabal)) <**> helper )) <*> ( optional (toolVersionArgument Nothing (Just Cabal)) <**> helper ))
( progDesc "Download cabal assets for installation") ( progDesc "Download cabal assets for installation")
) )
@@ -101,7 +102,7 @@ prefetchP = subparser
"hls" "hls"
(info (info
(PrefetchHLS (PrefetchHLS
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))) <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)")))
<*> ( optional (toolVersionArgument Nothing (Just HLS)) <**> helper )) <*> ( optional (toolVersionArgument Nothing (Just HLS)) <**> helper ))
( progDesc "Download HLS assets for installation") ( progDesc "Download HLS assets for installation")
) )
@@ -110,14 +111,14 @@ prefetchP = subparser
"stack" "stack"
(info (info
(PrefetchStack (PrefetchStack
<$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))) <$> fmap PrefetchOptions (optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)")))
<*> ( optional (toolVersionArgument Nothing (Just Stack)) <**> helper )) <*> ( optional (toolVersionArgument Nothing (Just Stack)) <**> helper ))
( progDesc "Download stack assets for installation") ( progDesc "Download stack assets for installation")
) )
<> <>
command command
"metadata" "metadata"
(PrefetchMetadata <$ info (const PrefetchMetadata <$> info
helper helper
( progDesc "Download ghcup's metadata, needed for various operations") ( progDesc "Download ghcup's metadata, needed for various operations")
) )
@@ -161,7 +162,7 @@ type PrefetchEffects = '[ TagNotFound
runPrefetch :: MonadUnliftIO m runPrefetch :: MonadUnliftIO m
=> (ReaderT AppState m (VEither PrefetchEffects a) -> m (VEither PrefetchEffects a)) => (ReaderT AppState m (VEither PrefetchEffects a) -> m (VEither PrefetchEffects a))
-> Excepts PrefetchEffects (ResourceT (ReaderT AppState m)) a -> (Excepts PrefetchEffects (ResourceT (ReaderT AppState m)) a)
-> m (VEither PrefetchEffects a) -> m (VEither PrefetchEffects a)
runPrefetch runAppState = runPrefetch runAppState =
runAppState runAppState
@@ -196,20 +197,20 @@ prefetch prefetchCommand runAppState runLogger =
if pfGHCSrc if pfGHCSrc
then liftE $ fetchGHCSrc (_tvVersion v) pfCacheDir then liftE $ fetchGHCSrc (_tvVersion v) pfCacheDir
else liftE $ fetchToolBindist (_tvVersion v) GHC pfCacheDir else liftE $ fetchToolBindist (_tvVersion v) GHC pfCacheDir
PrefetchCabal PrefetchOptions {pfCacheDir} mt -> do PrefetchCabal (PrefetchOptions {pfCacheDir}) mt -> do
forM_ pfCacheDir (liftIO . createDirRecursive') forM_ pfCacheDir (liftIO . createDirRecursive')
(v, _) <- liftE $ fromVersion mt Cabal (v, _) <- liftE $ fromVersion mt Cabal
liftE $ fetchToolBindist (_tvVersion v) Cabal pfCacheDir liftE $ fetchToolBindist (_tvVersion v) Cabal pfCacheDir
PrefetchHLS PrefetchOptions {pfCacheDir} mt -> do PrefetchHLS (PrefetchOptions {pfCacheDir}) mt -> do
forM_ pfCacheDir (liftIO . createDirRecursive') forM_ pfCacheDir (liftIO . createDirRecursive')
(v, _) <- liftE $ fromVersion mt HLS (v, _) <- liftE $ fromVersion mt HLS
liftE $ fetchToolBindist (_tvVersion v) HLS pfCacheDir liftE $ fetchToolBindist (_tvVersion v) HLS pfCacheDir
PrefetchStack PrefetchOptions {pfCacheDir} mt -> do PrefetchStack (PrefetchOptions {pfCacheDir}) mt -> do
forM_ pfCacheDir (liftIO . createDirRecursive') forM_ pfCacheDir (liftIO . createDirRecursive')
(v, _) <- liftE $ fromVersion mt Stack (v, _) <- liftE $ fromVersion mt Stack
liftE $ fetchToolBindist (_tvVersion v) Stack pfCacheDir liftE $ fetchToolBindist (_tvVersion v) Stack pfCacheDir
PrefetchMetadata -> do PrefetchMetadata -> do
_ <- liftE getDownloadsF _ <- liftE $ getDownloadsF
pure "" pure ""
) >>= \case ) >>= \case
VRight _ -> do VRight _ -> do

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -127,11 +128,11 @@ rmFooter = [s|Discussion:
--------------------------- ---------------------------
type RmEffects = '[ NotInstalled, UninstallFailed ] type RmEffects = '[ NotInstalled ]
runRm :: (ReaderT env m (VEither RmEffects a) -> m (VEither RmEffects a)) runRm :: (ReaderT env m (VEither RmEffects a) -> m (VEither RmEffects a))
-> Excepts RmEffects (ReaderT env m) a -> (Excepts RmEffects (ReaderT env m) a)
-> m (VEither RmEffects a) -> m (VEither RmEffects a)
runRm runAppState = runRm runAppState =
runAppState runAppState
@@ -151,7 +152,7 @@ rm :: ( Monad m
, MonadUnliftIO m , MonadUnliftIO m
, MonadFail m , MonadFail m
) )
=> Either RmCommand RmOptions => (Either RmCommand RmOptions)
-> (ReaderT AppState m (VEither RmEffects (Maybe VersionInfo)) -> (ReaderT AppState m (VEither RmEffects (Maybe VersionInfo))
-> m (VEither RmEffects (Maybe VersionInfo))) -> m (VEither RmEffects (Maybe VersionInfo)))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())

View File

@@ -1,460 +0,0 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
module GHCup.OptParse.Run where
import GHCup
import GHCup.Utils
import GHCup.Utils.Prelude
import GHCup.Utils.File
import GHCup.OptParse.Common
import GHCup.Errors
import GHCup.Types
import GHCup.Types.Optics
import GHCup.Utils.Logger
import GHCup.Utils.String.QQ
import Control.Exception.Safe ( MonadMask, MonadCatch )
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
import Codec.Archive
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import Data.Functor
import Data.Maybe (isNothing)
import Data.List ( intercalate )
import Haskus.Utils.Variant.Excepts
import Options.Applicative hiding ( style )
import Prelude hiding ( appendFile )
import System.FilePath
import System.Environment
import System.Exit
import Text.PrettyPrint.HughesPJClass ( prettyShow )
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
#ifndef IS_WINDOWS
import qualified System.Posix.Process as SPP
#endif
---------------
--[ Options ]--
---------------
data RunOptions = RunOptions
{ runAppendPATH :: Bool
, runInstTool' :: Bool
, runGHCVer :: Maybe ToolVersion
, runCabalVer :: Maybe ToolVersion
, runHLSVer :: Maybe ToolVersion
, runStackVer :: Maybe ToolVersion
, runBinDir :: Maybe FilePath
, runQuick :: Bool
, runCOMMAND :: [String]
}
---------------
--[ Parsers ]--
---------------
runOpts :: Parser RunOptions
runOpts =
RunOptions
<$> switch
(short 'a' <> long "append" <> help "Append bin/ dir to PATH instead of prepending (this means that e.g. a system installation may take precedence)")
<*> switch
(short 'i' <> long "install" <> help "Install the tool, if missing")
<*> optional
(option
(eitherReader toolVersionEither)
(metavar "GHC_VERSION" <> long "ghc" <> help "The ghc version"
<> completer (tagCompleter GHC [])
<> (completer $ versionCompleter Nothing GHC)
)
)
<*> optional
(option
(eitherReader toolVersionEither)
(metavar "CABAL_VERSION" <> long "cabal" <> help "The cabal version"
<> completer (tagCompleter Cabal [])
<> (completer $ versionCompleter Nothing Cabal)
)
)
<*> optional
(option
(eitherReader toolVersionEither)
(metavar "HLS_VERSION" <> long "hls" <> help "The HLS version"
<> completer (tagCompleter HLS [])
<> (completer $ versionCompleter Nothing HLS)
)
)
<*> optional
(option
(eitherReader toolVersionEither)
(metavar "STACK_VERSION" <> long "stack" <> help "The stack version"
<> completer (tagCompleter Stack [])
<> (completer $ versionCompleter Nothing Stack)
)
)
<*> optional
(option
(eitherReader isolateParser)
( short 'b'
<> long "bindir"
<> metavar "DIR"
<> help "directory where to create the tool symlinks (default: newly created system temp dir)"
<> completer (bashCompleter "directory")
)
)
<*> switch
(short 'q' <> long "quick" <> help "Avoid any expensive work (such as downloads, version/tag resolution etc.). Disables --install.")
<*> many (argument str (metavar "COMMAND" <> help "The command to run, with arguments (use longopts --). If omitted, just prints the created bin/ dir to stdout and exits."))
--------------
--[ Footer ]--
--------------
runFooter :: String
runFooter = [s|Discussion:
Adds the given tools to a dedicated bin/ directory and adds them to PATH, exposing
the relevant binaries, then executes a command.
Examples:
# run VSCode with all latest toolchain exposed, installing missing versions if necessary
ghcup run --ghc latest --cabal latest --hls latest --stack latest --install -- code Setup.hs
# create a custom toolchain bin/ dir with GHC and cabal that can be manually added to PATH
ghcup run --ghc 8.10.7 --cabal 3.2.0.0 --bindir $HOME/toolchain/bin
# run a specific ghc version
ghcup run --ghc 8.10.7 -- ghc --version|]
---------------------------
--[ Effect interpreters ]--
---------------------------
type RunEffects = '[ AlreadyInstalled
, UnknownArchive
, ArchiveResult
, FileDoesNotExistError
, CopyError
, NotInstalled
, DirNotEmpty
, NoDownload
, NotInstalled
, BuildFailed
, TagNotFound
, DigestError
, GPGError
, DownloadFailed
, TarDirDoesNotExist
, NextVerNotFound
, NoToolVersionSet
, FileAlreadyExistsError
, ProcessError
, UninstallFailed
]
runLeanRUN :: (MonadUnliftIO m, MonadIO m)
=> LeanAppState
-> Excepts RunEffects (ReaderT LeanAppState m) a
-> m (VEither RunEffects a)
runLeanRUN leanAppstate =
-- Don't use runLeanAppState here, which is disabled on windows.
-- This is the only command on all platforms that doesn't need full appstate.
flip runReaderT leanAppstate
. runE
@RunEffects
runRUN :: MonadUnliftIO m
=> IO AppState
-> Excepts RunEffects (ResourceT (ReaderT AppState m)) a
-> m (VEither RunEffects a)
runRUN appState action' = do
s' <- liftIO appState
flip runReaderT s'
. runResourceT
. runE
@RunEffects
$ action'
------------------
--[ Entrypoint ]--
------------------
run :: forall m.
( MonadFail m
, MonadMask m
, MonadCatch m
, MonadIO m
, MonadUnliftIO m
)
=> RunOptions
-> IO AppState
-> LeanAppState
-> (ReaderT LeanAppState m () -> m ())
-> m ExitCode
run RunOptions{..} runAppState leanAppstate runLogger = do
r <- if not runQuick
then runRUN runAppState $ do
toolchain <- liftE resolveToolchainFull
tmp <- liftIO $ createTmpDir toolchain
liftE $ installToolChainFull toolchain tmp
pure tmp
else runLeanRUN leanAppstate $ do
toolchain <- resolveToolchain
tmp <- liftIO $ createTmpDir toolchain
liftE $ installToolChain toolchain tmp
pure tmp
case r of
VRight tmp -> do
case runCOMMAND of
[] -> do
liftIO $ putStr tmp
pure ExitSuccess
(cmd:args) -> do
newEnv <- liftIO $ addToPath tmp
#ifndef IS_WINDOWS
void $ liftIO $ SPP.executeFile cmd True args (Just newEnv)
pure ExitSuccess
#else
r' <- runLeanRUN leanAppstate $ liftE $ lEM @_ @'[ProcessError] $ exec cmd args Nothing (Just newEnv)
case r' of
VRight _ -> pure ExitSuccess
VLeft e -> do
runLogger $ logError $ T.pack $ prettyShow e
pure $ ExitFailure 28
#endif
VLeft e -> do
runLogger $ logError $ T.pack $ prettyShow e
pure $ ExitFailure 27
where
createTmpDir :: Toolchain -> IO FilePath
createTmpDir toolchain =
case runBinDir of
Just bindir -> do
createDirRecursive' bindir
canonicalizePath bindir
Nothing -> do
d <- predictableTmpDir toolchain
createDirRecursive' d
canonicalizePath d
-- TODO: doesn't work for cross
resolveToolchainFull :: ( MonadFail m
, MonadThrow m
, MonadIO m
, MonadCatch m
)
=> Excepts
'[ TagNotFound
, NextVerNotFound
, NoToolVersionSet
] (ResourceT (ReaderT AppState m)) Toolchain
resolveToolchainFull = do
ghcVer <- forM runGHCVer $ \ver -> do
(v, _) <- liftE $ fromVersion (Just ver) GHC
pure v
cabalVer <- forM runCabalVer $ \ver -> do
(v, _) <- liftE $ fromVersion (Just ver) Cabal
pure v
hlsVer <- forM runHLSVer $ \ver -> do
(v, _) <- liftE $ fromVersion (Just ver) HLS
pure v
stackVer <- forM runStackVer $ \ver -> do
(v, _) <- liftE $ fromVersion (Just ver) Stack
pure v
pure Toolchain{..}
resolveToolchain = do
ghcVer <- case runGHCVer of
Just (ToolVersion v) -> pure $ Just v
Nothing -> pure Nothing
_ -> fail "Internal error"
cabalVer <- case runCabalVer of
Just (ToolVersion v) -> pure $ Just v
Nothing -> pure Nothing
_ -> fail "Internal error"
hlsVer <- case runHLSVer of
Just (ToolVersion v) -> pure $ Just v
Nothing -> pure Nothing
_ -> fail "Internal error"
stackVer <- case runStackVer of
Just (ToolVersion v) -> pure $ Just v
Nothing -> pure Nothing
_ -> fail "Internal error"
pure Toolchain{..}
installToolChainFull :: ( MonadFail m
, MonadThrow m
, MonadIO m
, MonadCatch m
)
=> Toolchain
-> FilePath
-> Excepts
'[ TagNotFound
, NextVerNotFound
, NoToolVersionSet
, UnknownArchive
, TarDirDoesNotExist
, ProcessError
, NotInstalled
, NoDownload
, GPGError
, DownloadFailed
, DirNotEmpty
, DigestError
, BuildFailed
, ArchiveResult
, AlreadyInstalled
, FileAlreadyExistsError
, CopyError
, UninstallFailed
] (ResourceT (ReaderT AppState m)) ()
installToolChainFull Toolchain{..} tmp = do
forM_ [(GHC,) <$> ghcVer, (Cabal,) <$> cabalVer, (HLS,) <$> hlsVer, (Stack,) <$> stackVer] $ \mt -> do
isInstalled <- maybe (pure False) (\(tool, v) -> lift $ checkIfToolInstalled' tool v) mt
case mt of
Just (GHC, v) -> do
unless isInstalled $ when (runInstTool' && isNothing (_tvTarget v)) $ void $ liftE $ installGHCBin
(_tvVersion v)
GHCupInternal
False
setTool GHC v tmp
Just (Cabal, v) -> do
unless isInstalled $ when runInstTool' $ void $ liftE $ installCabalBin
(_tvVersion v)
GHCupInternal
False
setTool Cabal v tmp
Just (Stack, v) -> do
unless isInstalled $ when runInstTool' $ void $ liftE $ installStackBin
(_tvVersion v)
GHCupInternal
False
setTool Stack v tmp
Just (HLS, v) -> do
unless isInstalled $ when runInstTool' $ void $ liftE $ installHLSBin
(_tvVersion v)
GHCupInternal
False
setTool HLS v tmp
_ -> pure ()
installToolChain :: ( MonadFail m
, MonadThrow m
, MonadIO m
, MonadCatch m
)
=> Toolchain
-> FilePath
-> Excepts '[NotInstalled] (ReaderT LeanAppState m) ()
installToolChain Toolchain{..} tmp = do
forM_ [(GHC,) <$> ghcVer, (Cabal,) <$> cabalVer, (HLS,) <$> hlsVer, (Stack,) <$> stackVer] $ \mt -> do
case mt of
Just (GHC, v) -> setTool GHC v tmp
Just (Cabal, v) -> setTool Cabal v tmp
Just (Stack, v) -> setTool Stack v tmp
Just (HLS, v) -> setTool HLS v tmp
_ -> pure ()
setTool tool v tmp =
case tool of
GHC -> do
void $ liftE $ setGHC v SetGHC_XYZ (Just tmp)
void $ liftE $ setGHC v SetGHCOnly (Just tmp)
Cabal -> do
bin <- liftE $ whereIsTool Cabal v
cbin <- liftIO $ canonicalizePath bin
lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("cabal" <.> exeExt))
Stack -> do
bin <- liftE $ whereIsTool Stack v
cbin <- liftIO $ canonicalizePath bin
lift $ createLink (relativeSymlink tmp cbin) (tmp </> ("stack" <.> exeExt))
HLS -> do
Dirs {..} <- getDirs
let v' = _tvVersion v
legacy <- isLegacyHLS v'
if legacy
then do
-- TODO: factor this out
hlsWrapper <- liftE @_ @'[NotInstalled] $ hlsWrapperBinary v' !? (NotInstalled HLS (mkTVer v'))
cw <- liftIO $ canonicalizePath (binDir </> hlsWrapper)
lift $ createLink (relativeSymlink tmp cw) (tmp </> takeFileName cw)
hlsBins <- hlsServerBinaries v' Nothing >>= liftIO . traverse (canonicalizePath . (binDir </>))
forM_ hlsBins $ \bin ->
lift $ createLink (relativeSymlink tmp bin) (tmp </> takeFileName bin)
liftE $ setHLS (_tvVersion v) SetHLSOnly (Just tmp)
else do
liftE $ setHLS (_tvVersion v) SetHLS_XYZ (Just tmp)
liftE $ setHLS (_tvVersion v) SetHLSOnly (Just tmp)
GHCup -> pure ()
addToPath path = do
cEnv <- Map.fromList <$> getEnvironment
let paths = ["PATH", "Path"]
curPaths = (\x -> maybe [] splitSearchPath (Map.lookup x cEnv)) =<< paths
newPath = intercalate [searchPathSeparator] (if runAppendPATH then (curPaths ++ [path]) else (path : curPaths))
envWithoutPath = foldr (\x y -> Map.delete x y) cEnv paths
pathVar = if isWindows then "Path" else "PATH"
envWithNewPath = Map.toList $ Map.insert pathVar newPath envWithoutPath
liftIO $ setEnv pathVar newPath
return envWithNewPath
predictableTmpDir (Toolchain Nothing Nothing Nothing Nothing) =
liftIO (getTemporaryDirectory >>= \tmp -> pure (tmp </> "ghcup-none"))
predictableTmpDir Toolchain{..} = do
tmp <- getTemporaryDirectory
pure $ tmp
</> ("ghcup-" <> intercalate "_"
( maybe [] ( (:[]) . ("ghc-" <>) . T.unpack . tVerToText) ghcVer
<> maybe [] ( (:[]) . ("cabal-" <>) . T.unpack . tVerToText) cabalVer
<> maybe [] ( (:[]) . ("hls-" <>) . T.unpack . tVerToText) hlsVer
<> maybe [] ( (:[]) . ("stack-" <>) . T.unpack . tVerToText) stackVer
)
)
-------------------------
--[ Other local types ]--
-------------------------
data Toolchain = Toolchain
{ ghcVer :: Maybe GHCTargetVersion
, cabalVer :: Maybe GHCTargetVersion
, hlsVer :: Maybe GHCTargetVersion
, stackVer :: Maybe GHCTargetVersion
}

View File

@@ -2,6 +2,8 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
@@ -40,7 +42,6 @@ import Text.PrettyPrint.HughesPJClass ( prettyShow )
import qualified Data.Text as T import qualified Data.Text as T
import Data.Bifunctor (second) import Data.Bifunctor (second)
import Control.Exception.Safe (MonadMask) import Control.Exception.Safe (MonadMask)
import GHCup.Types.Optics
@@ -186,7 +187,7 @@ type SetGHCEffects = '[ FileDoesNotExistError
, NoToolVersionSet] , NoToolVersionSet]
runSetGHC :: (ReaderT env m (VEither SetGHCEffects a) -> m (VEither SetGHCEffects a)) runSetGHC :: (ReaderT env m (VEither SetGHCEffects a) -> m (VEither SetGHCEffects a))
-> Excepts SetGHCEffects (ReaderT env m) a -> (Excepts SetGHCEffects (ReaderT env m) a)
-> m (VEither SetGHCEffects a) -> m (VEither SetGHCEffects a)
runSetGHC runAppState = runSetGHC runAppState =
runAppState runAppState
@@ -200,7 +201,7 @@ type SetCabalEffects = '[ NotInstalled
, NoToolVersionSet] , NoToolVersionSet]
runSetCabal :: (ReaderT env m (VEither SetCabalEffects a) -> m (VEither SetCabalEffects a)) runSetCabal :: (ReaderT env m (VEither SetCabalEffects a) -> m (VEither SetCabalEffects a))
-> Excepts SetCabalEffects (ReaderT env m) a -> (Excepts SetCabalEffects (ReaderT env m) a)
-> m (VEither SetCabalEffects a) -> m (VEither SetCabalEffects a)
runSetCabal runAppState = runSetCabal runAppState =
runAppState runAppState
@@ -214,7 +215,7 @@ type SetHLSEffects = '[ NotInstalled
, NoToolVersionSet] , NoToolVersionSet]
runSetHLS :: (ReaderT env m (VEither SetHLSEffects a) -> m (VEither SetHLSEffects a)) runSetHLS :: (ReaderT env m (VEither SetHLSEffects a) -> m (VEither SetHLSEffects a))
-> Excepts SetHLSEffects (ReaderT env m) a -> (Excepts SetHLSEffects (ReaderT env m) a)
-> m (VEither SetHLSEffects a) -> m (VEither SetHLSEffects a)
runSetHLS runAppState = runSetHLS runAppState =
runAppState runAppState
@@ -228,7 +229,7 @@ type SetStackEffects = '[ NotInstalled
, NoToolVersionSet] , NoToolVersionSet]
runSetStack :: (ReaderT env m (VEither SetStackEffects a) -> m (VEither SetStackEffects a)) runSetStack :: (ReaderT env m (VEither SetStackEffects a) -> m (VEither SetStackEffects a))
-> Excepts SetStackEffects (ReaderT env m) a -> (Excepts SetStackEffects (ReaderT env m) a)
-> m (VEither SetStackEffects a) -> m (VEither SetStackEffects a)
runSetStack runAppState = runSetStack runAppState =
runAppState runAppState
@@ -242,18 +243,15 @@ runSetStack runAppState =
------------------- -------------------
set :: forall m env. set :: forall m . ( Monad m
( Monad m
, MonadMask m , MonadMask m
, MonadUnliftIO m , MonadUnliftIO m
, MonadFail m , MonadFail m
, HasDirs env
, HasLog env
) )
=> Either SetCommand SetOptions => Either SetCommand SetOptions
-> (forall eff . ReaderT AppState m (VEither eff GHCTargetVersion) -> (forall eff . ReaderT AppState m (VEither eff GHCTargetVersion)
-> m (VEither eff GHCTargetVersion)) -> m (VEither eff GHCTargetVersion))
-> (forall eff. ReaderT env m (VEither eff GHCTargetVersion) -> (forall eff . ReaderT LeanAppState m (VEither eff GHCTargetVersion)
-> m (VEither eff GHCTargetVersion)) -> m (VEither eff GHCTargetVersion))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
@@ -271,10 +269,10 @@ set setCommand runAppState runLeanAppState runLogger = case setCommand of
-> m ExitCode -> m ExitCode
setGHC' SetOptions{ sToolVer } = setGHC' SetOptions{ sToolVer } =
case sToolVer of case sToolVer of
(SetToolVersion v) -> runSetGHC runLeanAppState (liftE $ setGHC v SetGHCOnly Nothing >> pure v) (SetToolVersion v) -> runSetGHC runLeanAppState (liftE $ setGHC v SetGHCOnly >> pure v)
_ -> runSetGHC runAppState (do _ -> runSetGHC runAppState (do
v <- liftE $ fst <$> fromVersion' sToolVer GHC v <- liftE $ fst <$> fromVersion' sToolVer GHC
liftE $ setGHC v SetGHCOnly Nothing liftE $ setGHC v SetGHCOnly
) )
>>= \case >>= \case
VRight GHCTargetVersion{..} -> do VRight GHCTargetVersion{..} -> do
@@ -311,10 +309,10 @@ set setCommand runAppState runLeanAppState runLogger = case setCommand of
-> m ExitCode -> m ExitCode
setHLS' SetOptions{ sToolVer } = setHLS' SetOptions{ sToolVer } =
case sToolVer of case sToolVer of
(SetToolVersion v) -> runSetHLS runLeanAppState (liftE $ setHLS (_tvVersion v) SetHLSOnly Nothing >> pure v) (SetToolVersion v) -> runSetHLS runLeanAppState (liftE $ setHLS (_tvVersion v) >> pure v)
_ -> runSetHLS runAppState (do _ -> runSetHLS runAppState (do
v <- liftE $ fst <$> fromVersion' sToolVer HLS v <- liftE $ fst <$> fromVersion' sToolVer HLS
liftE $ setHLS (_tvVersion v) SetHLSOnly Nothing liftE $ setHLS (_tvVersion v)
pure v pure v
) )
>>= \case >>= \case

View File

@@ -2,9 +2,12 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE QuasiQuotes #-}
module GHCup.OptParse.ToolRequirements where module GHCup.OptParse.ToolRequirements where
@@ -12,7 +15,6 @@ module GHCup.OptParse.ToolRequirements where
import GHCup.Errors import GHCup.Errors
import GHCup.Types import GHCup.Types
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.String.QQ
#if !MIN_VERSION_base(4,13,0) #if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail ) import Control.Monad.Fail ( MonadFail )
@@ -36,41 +38,6 @@ import System.IO
---------------
--[ Options ]--
---------------
data ToolReqOpts = ToolReqOpts
{ tlrRaw :: Bool
}
---------------
--[ Parsers ]--
---------------
toolReqP :: Parser ToolReqOpts
toolReqP =
ToolReqOpts
<$> switch (short 'r' <> long "raw-format" <> help "machine-parsable format")
--------------
--[ Footer ]--
--------------
toolReqFooter :: String
toolReqFooter = [s|Discussion:
Print tool requirements on the current platform.
If you want to pass this to your package manage, use '--raw-format'.|]
--------------------------- ---------------------------
@@ -82,7 +49,7 @@ type ToolRequirementsEffects = '[ NoCompatiblePlatform , DistroNotFound , NoTool
runToolRequirements :: (ReaderT env m (VEither ToolRequirementsEffects a) -> m (VEither ToolRequirementsEffects a)) runToolRequirements :: (ReaderT env m (VEither ToolRequirementsEffects a) -> m (VEither ToolRequirementsEffects a))
-> Excepts ToolRequirementsEffects (ReaderT env m) a -> (Excepts ToolRequirementsEffects (ReaderT env m) a)
-> m (VEither ToolRequirementsEffects a) -> m (VEither ToolRequirementsEffects a)
runToolRequirements runAppState = runToolRequirements runAppState =
runAppState runAppState
@@ -103,17 +70,14 @@ toolRequirements :: ( Monad m
, MonadFail m , MonadFail m
, Alternative m , Alternative m
) )
=> ToolReqOpts => (ReaderT AppState m (VEither ToolRequirementsEffects ()) -> m (VEither ToolRequirementsEffects ()))
-> (ReaderT AppState m (VEither ToolRequirementsEffects ()) -> m (VEither ToolRequirementsEffects ()))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
toolRequirements ToolReqOpts{..} runAppState runLogger = runToolRequirements runAppState (do toolRequirements runAppState runLogger = runToolRequirements runAppState (do
GHCupInfo { .. } <- lift getGHCupInfo GHCupInfo { .. } <- lift getGHCupInfo
platform' <- liftE getPlatform platform' <- liftE getPlatform
req <- getCommonRequirements platform' _toolRequirements ?? NoToolRequirements req <- getCommonRequirements platform' _toolRequirements ?? NoToolRequirements
if tlrRaw liftIO $ T.hPutStr stdout (prettyRequirements req)
then liftIO $ T.hPutStr stdout (rawRequirements req)
else liftIO $ T.hPutStr stdout (prettyRequirements req)
) )
>>= \case >>= \case
VRight _ -> pure ExitSuccess VRight _ -> pure ExitSuccess

View File

@@ -2,6 +2,7 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
@@ -35,7 +36,6 @@ import Text.PrettyPrint.HughesPJClass ( prettyShow )
import qualified Data.Text as T import qualified Data.Text as T
import Control.Exception.Safe (MonadMask) import Control.Exception.Safe (MonadMask)
import GHCup.Types.Optics
@@ -72,7 +72,7 @@ data UnsetOptions = UnsetOptions
unsetParser :: Parser UnsetCommand unsetParser :: Parser UnsetCommand
unsetParser = unsetParser =
subparser (subparser
( command ( command
"ghc" "ghc"
( UnsetGHC ( UnsetGHC
@@ -110,6 +110,7 @@ unsetParser =
) )
) )
) )
)
where where
unsetGHCFooter :: String unsetGHCFooter :: String
unsetGHCFooter = [s|Discussion: unsetGHCFooter = [s|Discussion:
@@ -155,7 +156,7 @@ type UnsetEffects = '[ NotInstalled ]
runUnsetGHC :: (ReaderT env m (VEither UnsetEffects a) -> m (VEither UnsetEffects a)) runUnsetGHC :: (ReaderT env m (VEither UnsetEffects a) -> m (VEither UnsetEffects a))
-> Excepts UnsetEffects (ReaderT env m) a -> (Excepts UnsetEffects (ReaderT env m) a)
-> m (VEither UnsetEffects a) -> m (VEither UnsetEffects a)
runUnsetGHC runLeanAppState = runUnsetGHC runLeanAppState =
runLeanAppState runLeanAppState
@@ -174,11 +175,9 @@ unset :: ( Monad m
, MonadMask m , MonadMask m
, MonadUnliftIO m , MonadUnliftIO m
, MonadFail m , MonadFail m
, HasDirs env
, HasLog env
) )
=> UnsetCommand => UnsetCommand
-> (ReaderT env m (VEither UnsetEffects ()) -> (ReaderT LeanAppState m (VEither UnsetEffects ())
-> m (VEither UnsetEffects ())) -> m (VEither UnsetEffects ()))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode

View File

@@ -3,6 +3,9 @@
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -65,15 +68,13 @@ upgradeOptsP =
flag' flag'
UpgradeInplace UpgradeInplace
(short 'i' <> long "inplace" <> help (short 'i' <> long "inplace" <> help
"Upgrade ghcup in-place" "Upgrade ghcup in-place (wherever it's at)"
) )
<|> <|> ( UpgradeAt
( UpgradeAt
<$> option <$> option
str str
(short 't' <> long "target" <> metavar "TARGET_DIR" <> help (short 't' <> long "target" <> metavar "TARGET_DIR" <> help
"Absolute filepath to write ghcup into" "Absolute filepath to write ghcup into"
<> completer (bashCompleter "file")
) )
) )
<|> pure UpgradeGHCupDir <|> pure UpgradeGHCupDir
@@ -93,7 +94,6 @@ type UpgradeEffects = '[ DigestError
, FileDoesNotExistError , FileDoesNotExistError
, CopyError , CopyError
, DownloadFailed , DownloadFailed
, GHCupShadowed
] ]
@@ -116,25 +116,24 @@ runUpgrade runAppState =
upgrade :: ( Monad m upgrade :: ( Monad m
, MonadMask m , MonadMask m
, MonadUnliftIO m , MonadUnliftIO m
, MonadFail m , MonadFail m
) )
=> UpgradeOpts => UpgradeOpts
-> Bool -> Bool
-> Bool
-> Dirs
-> (forall a. ReaderT AppState m (VEither UpgradeEffects a) -> m (VEither UpgradeEffects a)) -> (forall a. ReaderT AppState m (VEither UpgradeEffects a) -> m (VEither UpgradeEffects a))
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
upgrade uOpts force' fatal Dirs{..} runAppState runLogger = do upgrade uOpts force' runAppState runLogger = do
VRight Dirs{ .. } <- runAppState (VRight <$> getDirs)
target <- case uOpts of target <- case uOpts of
UpgradeInplace -> Just <$> liftIO getExecutablePath UpgradeInplace -> Just <$> liftIO getExecutablePath
(UpgradeAt p) -> pure $ Just p (UpgradeAt p) -> pure $ Just p
UpgradeGHCupDir -> pure (Just (binDir </> "ghcup" <> exeExt)) UpgradeGHCupDir -> pure (Just (binDir </> "ghcup" <> exeExt))
runUpgrade runAppState (do runUpgrade runAppState (do
v' <- liftE $ upgradeGHCup target force' fatal v' <- liftE $ upgradeGHCup target force'
GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
pure (v', dls) pure (v', dls)
) >>= \case ) >>= \case

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
@@ -17,7 +18,6 @@ import GHCup
import GHCup.Errors import GHCup.Errors
import GHCup.OptParse.Common import GHCup.OptParse.Common
import GHCup.Types import GHCup.Types
import GHCup.Utils
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.String.QQ import GHCup.Utils.String.QQ
@@ -113,7 +113,7 @@ whereisP = subparser
<> <>
command command
"ghcup" "ghcup"
(WhereisTool GHCup <$> info ( pure Nothing <**> helper ) ( progDesc "Get ghcup location" )) (WhereisTool GHCup <$> info ( (pure Nothing) <**> helper ) ( progDesc "Get ghcup location" ))
) <|> subparser ( commandGroup "Directory locations:" ) <|> subparser ( commandGroup "Directory locations:"
<> <>
command command
@@ -266,7 +266,7 @@ whereis :: ( Monad m
-> (ReaderT LeanAppState m () -> m ()) -> (ReaderT LeanAppState m () -> m ())
-> m ExitCode -> m ExitCode
whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
Dirs{ .. } <- runReaderT getDirs leanAppstate VRight Dirs{ .. } <- runAppState (VRight <$> getDirs)
case (whereisCommand, whereisOptions) of case (whereisCommand, whereisOptions) of
(WhereisTool tool (Just (ToolVersion v)), WhereisOptions{..}) -> (WhereisTool tool (Just (ToolVersion v)), WhereisOptions{..}) ->
runLeanWhereIs leanAppstate (do runLeanWhereIs leanAppstate (do
@@ -283,7 +283,7 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
runLogger $ logError $ T.pack $ prettyShow e runLogger $ logError $ T.pack $ prettyShow e
pure $ ExitFailure 30 pure $ ExitFailure 30
(WhereisTool tool whereVer, WhereisOptions{..}) -> do (WhereisTool tool whereVer, WhereisOptions{..}) ->
runWhereIs runAppState (do runWhereIs runAppState (do
(v, _) <- liftE $ fromVersion whereVer tool (v, _) <- liftE $ fromVersion whereVer tool
loc <- liftE $ whereIsTool tool v loc <- liftE $ whereIsTool tool v
@@ -300,7 +300,7 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
pure $ ExitFailure 30 pure $ ExitFailure 30
(WhereisBaseDir, _) -> do (WhereisBaseDir, _) -> do
liftIO $ putStr $ fromGHCupPath baseDir liftIO $ putStr baseDir
pure ExitSuccess pure ExitSuccess
(WhereisBinDir, _) -> do (WhereisBinDir, _) -> do
@@ -308,13 +308,13 @@ whereis whereisCommand whereisOptions runAppState leanAppstate runLogger = do
pure ExitSuccess pure ExitSuccess
(WhereisCacheDir, _) -> do (WhereisCacheDir, _) -> do
liftIO $ putStr $ fromGHCupPath cacheDir liftIO $ putStr cacheDir
pure ExitSuccess pure ExitSuccess
(WhereisLogsDir, _) -> do (WhereisLogsDir, _) -> do
liftIO $ putStr $ fromGHCupPath logsDir liftIO $ putStr logsDir
pure ExitSuccess pure ExitSuccess
(WhereisConfDir, _) -> do (WhereisConfDir, _) -> do
liftIO $ putStr $ fromGHCupPath confDir liftIO $ putStr confDir
pure ExitSuccess pure ExitSuccess

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
@@ -20,7 +21,6 @@ import GHCup.Download
import GHCup.Errors import GHCup.Errors
import GHCup.Platform import GHCup.Platform
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics hiding ( toolRequirements )
import GHCup.Utils import GHCup.Utils
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
@@ -40,7 +40,6 @@ import Data.Aeson.Encode.Pretty ( encodePretty )
import Data.Either import Data.Either
import Data.Functor import Data.Functor
import Data.Maybe import Data.Maybe
import Data.Versions
import GHC.IO.Encoding import GHC.IO.Encoding
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Language.Haskell.TH import Language.Haskell.TH
@@ -57,7 +56,6 @@ import qualified Data.ByteString as B
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import qualified GHCup.Types as Types
@@ -75,16 +73,15 @@ toSettings options = do
where where
mergeConf :: Options -> UserSettings -> Bool -> (Settings, KeyBindings) mergeConf :: Options -> UserSettings -> Bool -> (Settings, KeyBindings)
mergeConf Options{..} UserSettings{..} noColor = mergeConf Options{..} UserSettings{..} noColor =
let cache = fromMaybe (fromMaybe (Types.cache defaultSettings) uCache) optCache let cache = fromMaybe (fromMaybe False uCache) optCache
metaCache = fromMaybe (fromMaybe (Types.metaCache defaultSettings) uMetaCache) optMetaCache noVerify = fromMaybe (fromMaybe False uNoVerify) optNoVerify
noVerify = fromMaybe (fromMaybe (Types.noVerify defaultSettings) uNoVerify) optNoVerify verbose = fromMaybe (fromMaybe False uVerbose) optVerbose
verbose = fromMaybe (fromMaybe (Types.verbose defaultSettings) uVerbose) optVerbose keepDirs = fromMaybe (fromMaybe Errors uKeepDirs) optKeepDirs
keepDirs = fromMaybe (fromMaybe (Types.keepDirs defaultSettings) uKeepDirs) optKeepDirs
downloader = fromMaybe (fromMaybe defaultDownloader uDownloader) optsDownloader downloader = fromMaybe (fromMaybe defaultDownloader uDownloader) optsDownloader
keyBindings = maybe defaultKeyBindings mergeKeys uKeyBindings keyBindings = maybe defaultKeyBindings mergeKeys uKeyBindings
urlSource = maybe (fromMaybe (Types.urlSource defaultSettings) uUrlSource) (OwnSource . (:[]) . Right) optUrlSource urlSource = maybe (fromMaybe GHCupURL uUrlSource) OwnSource optUrlSource
noNetwork = fromMaybe (fromMaybe (Types.noNetwork defaultSettings) uNoNetwork) optNoNetwork noNetwork = fromMaybe (fromMaybe False uNoNetwork) optNoNetwork
gpgSetting = fromMaybe (fromMaybe (Types.gpgSetting defaultSettings) uGPGSetting) optGpg gpgSetting = fromMaybe (fromMaybe GPGNone uGPGSetting) optGpg
in (Settings {..}, keyBindings) in (Settings {..}, keyBindings)
#if defined(INTERNAL_DOWNLOADER) #if defined(INTERNAL_DOWNLOADER)
defaultDownloader = Internal defaultDownloader = Internal
@@ -115,7 +112,7 @@ plan_json = $( do
c <- B.readFile fp c <- B.readFile fp
(Just res) <- pure $ decodeStrict' @Value c (Just res) <- pure $ decodeStrict' @Value c
pure (fp, T.unpack $ decUTF8Safe' $ encodePretty res)) pure (fp, T.unpack $ decUTF8Safe' $ encodePretty res))
unless (null fp) $ qAddDependentFile fp when (not . null $ fp ) $ qAddDependentFile fp
pure . LitE . StringL $ c) pure . LitE . StringL $ c)
@@ -127,7 +124,8 @@ main = do
void enableAnsiSupport void enableAnsiSupport
let versionHelp = infoOption let versionHelp = infoOption
( "The GHCup Haskell installer, version " <> (head . lines $ describe_result) ( ("The GHCup Haskell installer, version " <>)
(head . lines $ describe_result)
) )
(long "version" <> help "Show version" <> hidden) (long "version" <> help "Show version" <> hidden)
let planJson = infoOption let planJson = infoOption
@@ -140,10 +138,7 @@ main = do
<> hidden <> hidden
) )
let listCommands = infoOption let listCommands = infoOption
("install set rm install-cabal list" "install set rm install-cabal list upgrade compile debug-info tool-requirements changelog"
<> " upgrade"
<> " compile debug-info tool-requirements changelog"
)
( long "list-commands" ( long "list-commands"
<> help "List available commands for shell completion" <> help "List available commands for shell completion"
<> internal <> internal
@@ -175,7 +170,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
(settings, keybindings) <- toSettings opt (settings, keybindings) <- toSettings opt
-- logger interpreter -- logger interpreter
logfile <- runReaderT initGHCupFileLogging dirs logfile <- flip runReaderT dirs initGHCupFileLogging
no_color <- isJust <$> lookupEnv "NO_COLOR" no_color <- isJust <$> lookupEnv "NO_COLOR"
let loggerConfig = LoggerConfig let loggerConfig = LoggerConfig
{ lcPrintDebug = verbose settings { lcPrintDebug = verbose settings
@@ -196,7 +191,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
------------------------- -------------------------
let appState = do appState = do
pfreq <- ( pfreq <- (
runLogger . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest runLogger . runE @'[NoCompatiblePlatform, NoCompatibleArch, DistroNotFound] . liftE $ platformRequest
) >>= \case ) >>= \case
@@ -209,7 +204,8 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
ghcupInfo <- ghcupInfo <-
( flip runReaderT leanAppstate ( flip runReaderT leanAppstate
. runE @'[DigestError, GPGError, JSONError , DownloadFailed, FileDoesNotExistError] . runE @'[DigestError, GPGError, JSONError , DownloadFailed, FileDoesNotExistError]
$ liftE getDownloadsF $ liftE
$ getDownloadsF
) )
>>= \case >>= \case
VRight r -> pure r VRight r -> pure r
@@ -219,47 +215,25 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
exitWith (ExitFailure 2) exitWith (ExitFailure 2)
let s' = AppState settings dirs keybindings ghcupInfo pfreq loggerConfig let s' = AppState settings dirs keybindings ghcupInfo pfreq loggerConfig
race_ (liftIO $ runReaderT cleanupTrash s') race_ (liftIO $ flip runReaderT s' cleanupTrash)
(threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack (fromGHCupPath recycleDir) <> " manually")) (threadDelay 5000000 >> runLogger (logWarn $ "Killing cleanup thread (exceeded 5s timeout)... please remove leftover files in " <> T.pack recycleDir <> " manually"))
case optCommand of case optCommand of
Nuke -> pure () Nuke -> pure ()
Whereis _ _ -> pure () Whereis _ _ -> pure ()
DInfo -> pure () DInfo -> pure ()
ToolRequirements _ -> pure () ToolRequirements -> pure ()
ChangeLog _ -> pure () ChangeLog _ -> pure ()
UnSet _ -> pure () UnSet _ -> pure ()
#if defined(BRICK) #if defined(BRICK)
Interactive -> pure () Interactive -> pure ()
#endif #endif
-- check for new tools _ -> lookupEnv "GHCUP_SKIP_UPDATE_CHECK" >>= \case
_ Nothing -> flip runReaderT s' checkForUpdates
| Just False <- optVerbose -> pure ()
| otherwise -> lookupEnv "GHCUP_SKIP_UPDATE_CHECK" >>= \case
Nothing -> void . flip runReaderT s' . runE @'[TagNotFound, NextVerNotFound, NoToolVersionSet] $ do
newTools <- lift checkForUpdates
forM_ newTools $ \newTool@(t, l) -> do
-- https://gitlab.haskell.org/haskell/ghcup-hs/-/issues/283
alreadyInstalling' <- alreadyInstalling optCommand newTool
when (not alreadyInstalling') $
case t of
GHCup -> runLogger $
logWarn ("New GHCup version available: "
<> prettyVer l
<> ". To upgrade, run 'ghcup upgrade'")
_ -> runLogger $
logWarn ("New "
<> T.pack (prettyShow t)
<> " version available. "
<> "To upgrade, run 'ghcup install "
<> T.pack (prettyShow t)
<> " "
<> prettyVer l
<> "'")
Just _ -> pure () Just _ -> pure ()
-- TODO: always run for windows -- TODO: always run for windows
siletRunLogger (flip runReaderT s' $ runE ensureGlobalTools) >>= \case (siletRunLogger $ flip runReaderT s' $ runE ensureGlobalTools) >>= \case
VRight _ -> pure () VRight _ -> pure ()
VLeft e -> do VLeft e -> do
runLogger runLogger
@@ -277,7 +251,7 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
#endif #endif
runAppState action' = do runAppState action' = do
s' <- liftIO appState s' <- liftIO appState
runReaderT action' s' flip runReaderT s' action'
----------------- -----------------
@@ -290,24 +264,23 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
s' <- appState s' <- appState
liftIO $ brickMain s' >> pure ExitSuccess liftIO $ brickMain s' >> pure ExitSuccess
#endif #endif
Install installCommand -> install installCommand settings appState runLogger Install installCommand -> install installCommand settings appState runLogger
InstallCabalLegacy iopts -> install (Left (InstallCabal iopts)) settings appState runLogger InstallCabalLegacy iopts -> install (Left (InstallCabal iopts)) settings appState runLogger
Set setCommand -> set setCommand runAppState runLeanAppState runLogger Set setCommand -> set setCommand runAppState runLeanAppState runLogger
UnSet unsetCommand -> unset unsetCommand runLeanAppState runLogger UnSet unsetCommand -> unset unsetCommand runLeanAppState runLogger
List lo -> list lo no_color runAppState List lo -> list lo no_color runAppState
Rm rmCommand -> rm rmCommand runAppState runLogger Rm rmCommand -> rm rmCommand runAppState runLogger
DInfo -> dinfo runAppState runLogger DInfo -> dinfo runAppState runLogger
Compile compileCommand -> compile compileCommand settings dirs runAppState runLogger Compile compileCommand -> compile compileCommand settings runAppState runLogger
Config configCommand -> config configCommand settings keybindings runLogger Config configCommand -> config configCommand settings keybindings runLogger
Whereis whereisOptions Whereis whereisOptions
whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger whereisCommand -> whereis whereisCommand whereisOptions runAppState leanAppstate runLogger
Upgrade uOpts force' fatal -> upgrade uOpts force' fatal dirs runAppState runLogger Upgrade uOpts force' -> upgrade uOpts force' runAppState runLogger
ToolRequirements topts -> toolRequirements topts runAppState runLogger ToolRequirements -> toolRequirements runAppState runLogger
ChangeLog changelogOpts -> changelog changelogOpts runAppState runLogger ChangeLog changelogOpts -> changelog changelogOpts runAppState runLogger
Nuke -> nuke appState runLogger Nuke -> nuke appState runLogger
Prefetch pfCom -> prefetch pfCom runAppState runLogger Prefetch pfCom -> prefetch pfCom runAppState runLogger
GC gcOpts -> gc gcOpts runAppState runLogger GC gcOpts -> gc gcOpts runAppState runLogger
Run runCommand -> run runCommand appState leanAppstate runLogger
case res of case res of
ExitSuccess -> pure () ExitSuccess -> pure ()
@@ -315,56 +288,4 @@ Report bugs at <https://gitlab.haskell.org/haskell/ghcup-hs/issues>|]
pure () pure ()
where
alreadyInstalling :: ( HasLog env
, MonadFail m
, MonadReader env m
, HasGHCupInfo env
, HasDirs env
, MonadThrow m
, MonadIO m
, MonadCatch m
)
=> Command
-> (Tool, Version)
-> Excepts
'[ TagNotFound
, NextVerNotFound
, NoToolVersionSet
] m Bool
alreadyInstalling (Install (Right InstallOptions{..})) (GHC, ver) = cmp' GHC instVer ver
alreadyInstalling (Install (Left (InstallGHC InstallOptions{..}))) (GHC, ver) = cmp' GHC instVer ver
alreadyInstalling (Install (Left (InstallCabal InstallOptions{..}))) (Cabal, ver) = cmp' Cabal instVer ver
alreadyInstalling (Install (Left (InstallHLS InstallOptions{..}))) (HLS, ver) = cmp' HLS instVer ver
alreadyInstalling (Install (Left (InstallStack InstallOptions{..}))) (Stack, ver) = cmp' Stack instVer ver
alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ ovewrwiteVer = Just over }))
(GHC, ver) = cmp' GHC (Just $ ToolVersion (mkTVer over)) ver
alreadyInstalling (Compile (CompileGHC GHCCompileOptions{ targetGhc = Left tver }))
(GHC, ver) = cmp' GHC (Just $ ToolVersion (mkTVer tver)) ver
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ ovewrwiteVer = Just over }))
(HLS, ver) = cmp' HLS (Just $ ToolVersion (mkTVer over)) ver
alreadyInstalling (Compile (CompileHLS HLSCompileOptions{ targetHLS = Left tver }))
(HLS, ver) = cmp' HLS (Just $ ToolVersion (mkTVer tver)) ver
alreadyInstalling (Upgrade _ _ _) (GHCup, _) = pure True
alreadyInstalling _ _ = pure False
cmp' :: ( HasLog env
, MonadFail m
, MonadReader env m
, HasGHCupInfo env
, HasDirs env
, MonadThrow m
, MonadIO m
, MonadCatch m
)
=> Tool
-> Maybe ToolVersion
-> Version
-> Excepts
'[ TagNotFound
, NextVerNotFound
, NoToolVersionSet
] m Bool
cmp' tool instVer ver = do
(v, _) <- liftE $ fromVersion instVer tool
pure (v == mkTVer ver)

View File

@@ -8,14 +8,7 @@ package ghcup
tests: True tests: True
flags: +tui flags: +tui
source-repository-package constraints: http-io-streams -brotli
type: git
location: https://github.com/bgamari/terminal-size.git
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
constraints: http-io-streams -brotli,
any.Cabal ==3.6.2.0,
any.aeson >= 2.0.1.0
package libarchive package libarchive
flags: -system-libarchive flags: -system-libarchive
@@ -26,9 +19,6 @@ package aeson-pretty
package cabal-plan package cabal-plan
flags: -exe flags: -exe
package aeson
flags: +ordered-keymap
allow-newer: base, ghc-prim, template-haskell, language-c allow-newer: base, ghc-prim, template-haskell, language-c
with-compiler: ghc-8.10.7 with-compiler: ghc-8.10.7

View File

@@ -1,50 +1,49 @@
active-repositories: hackage.haskell.org:merge active-repositories: hackage.haskell.org:merge
constraints: any.Cabal ==3.6.2.0, constraints: any.Cabal ==3.2.1.0 || ==3.6.1.0,
Cabal -bundled-binary-generic, Cabal -bundled-binary-generic,
any.HUnit ==1.6.2.0, any.HUnit ==1.6.2.0,
any.HsOpenSSL ==0.11.7.2, any.HsOpenSSL ==0.11.7.2,
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config, HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config,
any.OneTuple ==0.3.1, any.HsYAML ==0.2.1.0,
HsYAML -exe,
any.HsYAML-aeson ==0.2.0.0,
any.QuickCheck ==2.14.2, any.QuickCheck ==2.14.2,
QuickCheck -old-random +templatehaskell, QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
any.abstract-deque ==0.3, any.aeson ==1.5.6.0,
abstract-deque -usecas, aeson -bytestring-builder -cffi -developer -fast,
any.aeson ==2.0.3.0, any.aeson-pretty ==0.8.8,
aeson -cffi +ordered-keymap,
any.aeson-pretty ==0.8.9,
aeson-pretty +lib-only, aeson-pretty +lib-only,
any.alex ==3.2.7.1, any.alex ==3.2.6,
any.ansi-terminal ==0.11.1, alex +small_base,
any.ansi-terminal ==0.11,
ansi-terminal -example, ansi-terminal -example,
any.ansi-wl-pprint ==0.6.9, any.ansi-wl-pprint ==0.6.9,
ansi-wl-pprint -example, ansi-wl-pprint -example,
any.array ==0.5.4.0, any.array ==0.5.4.0,
any.assoc ==1.0.2, any.assoc ==1.0.2,
any.async ==2.2.4, any.async ==2.2.3,
async -bench, async -bench,
any.atomic-primops ==0.8.4, any.attoparsec ==0.13.2.5,
atomic-primops -debug,
any.attoparsec ==0.14.4,
attoparsec -developer, attoparsec -developer,
any.base ==4.14.3.0, any.base ==4.14.3.0,
any.base-compat ==0.12.1, any.base-compat ==0.12.0,
any.base-compat-batteries ==0.12.1, any.base-compat-batteries ==0.12.0,
any.base-orphans ==0.8.6, any.base-orphans ==0.8.5,
any.base16-bytestring ==1.0.2.0, any.base16-bytestring ==1.0.1.0,
any.base64-bytestring ==1.2.1.0, any.base64-bytestring ==1.1.0.0,
any.bifunctors ==5.5.11, any.bifunctors ==5.5.11,
bifunctors +semigroups +tagged, bifunctors +semigroups +tagged,
any.binary ==0.8.8.0, any.binary ==0.8.8.0,
any.blaze-builder ==0.4.2.2, any.blaze-builder ==0.4.2.1,
any.brick ==0.64.2, any.brick ==0.64.1,
brick -demos, brick -demos,
any.bytestring ==0.10.12.0, any.bytestring ==0.10.12.0,
any.bz2 ==1.0.1.0, any.bz2 ==1.0.1.0,
bz2 -cross +with-bzlib, bz2 -cross +with-bzlib,
any.c2hs ==0.28.8, any.c2hs ==0.28.8,
c2hs +base3 -regression, c2hs +base3 -regression,
any.cabal-plan ==0.7.2.1, any.cabal-plan ==0.7.2.0,
cabal-plan -_ -exe -license-report, cabal-plan -_ -exe -license-report,
any.call-stack ==0.4.0, any.call-stack ==0.4.0,
any.case-insensitive ==1.2.1.0, any.case-insensitive ==1.2.1.0,
@@ -52,7 +51,7 @@ constraints: any.Cabal ==3.6.2.0,
any.chs-cabal ==0.1.1.1, any.chs-cabal ==0.1.1.1,
any.chs-deps ==0.1.0.0, any.chs-deps ==0.1.0.0,
chs-deps -cross, chs-deps -cross,
any.clock ==0.8.3, any.clock ==0.8.2,
clock -llvm, clock -llvm,
any.colour ==2.3.6, any.colour ==2.3.6,
any.comonad ==5.0.8, any.comonad ==5.0.8,
@@ -66,10 +65,10 @@ constraints: any.Cabal ==3.6.2.0,
contravariant +semigroups +statevar +tagged, contravariant +semigroups +statevar +tagged,
any.cpphs ==1.20.9.1, any.cpphs ==1.20.9.1,
cpphs -old-locale, cpphs -old-locale,
any.cryptohash-sha1 ==0.11.101.0, any.cryptohash-sha1 ==0.11.100.1,
any.cryptohash-sha256 ==0.11.102.1, any.cryptohash-sha256 ==0.11.102.0,
cryptohash-sha256 -exe +use-cbits, cryptohash-sha256 -exe +use-cbits,
any.data-clist ==0.2, any.data-clist ==0.1.2.3,
any.data-fix ==0.3.2, any.data-fix ==0.3.2,
any.deepseq ==1.4.4.0, any.deepseq ==1.4.4.0,
any.directory ==1.3.6.0, any.directory ==1.3.6.0,
@@ -81,35 +80,28 @@ constraints: any.Cabal ==3.6.2.0,
any.exceptions ==0.10.4, any.exceptions ==0.10.4,
any.filepath ==1.4.2.1, any.filepath ==1.4.2.1,
any.free ==5.1.7, any.free ==5.1.7,
any.fusion-plugin-types ==0.1.0, any.generic-arbitrary ==0.1.0,
any.generic-arbitrary ==0.2.0,
any.ghc ==8.10.7,
any.ghc-boot ==8.10.7,
any.ghc-boot-th ==8.10.7, any.ghc-boot-th ==8.10.7,
any.ghc-byteorder ==4.11.0.0.10, any.ghc-byteorder ==4.11.0.0.10,
any.ghc-heap ==8.10.7,
any.ghc-prim ==0.6.1, any.ghc-prim ==0.6.1,
any.ghci ==8.10.7,
any.happy ==1.20.0, any.happy ==1.20.0,
any.hashable ==1.4.0.2, any.hashable ==1.3.3.0,
hashable +containers +integer-gmp -random-initial-seed, hashable +integer-gmp -random-initial-seed,
any.haskus-utils-data ==1.4, any.haskus-utils-data ==1.4,
any.haskus-utils-types ==1.5.1, any.haskus-utils-types ==1.5.1,
any.haskus-utils-variant ==3.2.1, any.haskus-utils-variant ==3.1,
any.heaps ==0.4, any.hsc2hs ==0.68.7,
any.hpc ==0.6.1.0,
any.hsc2hs ==0.68.8,
hsc2hs -in-ghc-tree, hsc2hs -in-ghc-tree,
any.hspec ==2.9.4, any.hspec ==2.7.10,
any.hspec-core ==2.9.4, any.hspec-core ==2.7.10,
any.hspec-discover ==2.9.4, any.hspec-discover ==2.7.10,
any.hspec-expectations ==0.8.2, any.hspec-expectations ==0.8.2,
any.hspec-golden-aeson ==0.9.0.0, any.hspec-golden-aeson ==0.9.0.0,
any.http-io-streams ==0.1.6.0, any.http-io-streams ==0.1.6.0,
http-io-streams -brotli +fast-xor, http-io-streams -brotli +fast-xor,
any.indexed-profunctors ==0.1.1, any.indexed-profunctors ==0.1.1,
any.indexed-traversable ==0.1.2, any.indexed-traversable ==0.1.1,
any.indexed-traversable-instances ==0.1.1, any.indexed-traversable-instances ==0.1,
any.integer-gmp ==1.0.3.0, any.integer-gmp ==1.0.3.0,
any.integer-logarithms ==1.0.3.1, any.integer-logarithms ==1.0.3.1,
integer-logarithms -check-bounds +integer-gmp, integer-logarithms -check-bounds +integer-gmp,
@@ -117,20 +109,16 @@ constraints: any.Cabal ==3.6.2.0,
io-streams +network -nointeractivetests +zlib, io-streams +network -nointeractivetests +zlib,
any.language-c ==0.9.0.1, any.language-c ==0.9.0.1,
language-c -allwarnings +iecfpextension +usebytestrings, language-c -allwarnings +iecfpextension +usebytestrings,
any.libarchive ==3.0.3.2, any.libarchive ==3.0.3.0,
libarchive -cross -low-memory +no-exe -system-libarchive, libarchive -cross -low-memory -system-libarchive,
any.libyaml-streamly ==0.2.1,
libyaml-streamly -no-unicode -system-libyaml,
any.lockfree-queue ==0.2.3.1,
any.lzma-static ==5.2.5.4, any.lzma-static ==5.2.5.4,
any.megaparsec ==9.2.0, any.megaparsec ==9.0.1,
megaparsec -dev, megaparsec -dev,
any.microlens ==0.4.12.0, any.microlens ==0.4.12.0,
any.microlens-mtl ==0.2.0.1, any.microlens-mtl ==0.2.0.1,
any.microlens-th ==0.4.3.10, any.microlens-th ==0.4.3.10,
any.monad-control ==1.0.3.1,
any.mtl ==2.2.2, any.mtl ==2.2.2,
any.network ==3.1.2.7, any.network ==3.1.2.2,
network -devel, network -devel,
any.network-uri ==2.6.4.1, any.network-uri ==2.6.4.1,
any.openssl-streams ==1.2.3.0, any.openssl-streams ==1.2.3.0,
@@ -139,9 +127,9 @@ constraints: any.Cabal ==3.6.2.0,
optics-core -explicit-generic-labels, optics-core -explicit-generic-labels,
any.optics-extra ==0.4, any.optics-extra ==0.4,
any.optics-th ==0.4, any.optics-th ==0.4,
any.optparse-applicative ==0.17.0.0, any.optparse-applicative ==0.16.1.0,
optparse-applicative +process, optparse-applicative +process,
any.os-release ==1.0.2.1, any.os-release ==1.0.2,
os-release -devel, os-release -devel,
any.parallel ==3.2.2.0, any.parallel ==3.2.2.0,
any.parsec ==3.1.14.0, any.parsec ==3.1.14.0,
@@ -150,56 +138,47 @@ constraints: any.Cabal ==3.6.2.0,
any.polyparse ==1.13, any.polyparse ==1.13,
any.pretty ==1.1.3.6, any.pretty ==1.1.3.6,
any.pretty-terminal ==0.1.0.0, any.pretty-terminal ==0.1.0.0,
any.primitive ==0.7.3.0, any.primitive ==0.7.2.0,
any.process ==1.6.13.2, any.process ==1.6.13.2,
any.profunctors ==5.6.2, any.profunctors ==5.6.2,
any.quickcheck-arbitrary-adt ==0.3.1.0, any.quickcheck-arbitrary-adt ==0.3.1.0,
any.quickcheck-io ==0.2.0, any.quickcheck-io ==0.2.0,
any.random ==1.2.1, any.random ==1.2.1,
any.recursion-schemes ==5.2.2.2, any.recursion-schemes ==5.2.2.1,
recursion-schemes +template-haskell, recursion-schemes +template-haskell,
any.regex-base ==0.94.0.2, any.regex-base ==0.94.0.1,
any.regex-posix ==0.96.0.1, any.regex-posix ==0.96.0.1,
regex-posix -_regex-posix-clib, regex-posix -_regex-posix-clib,
any.resourcet ==1.2.4.3, any.resourcet ==1.2.4.3,
any.retry ==0.8.1.2,
retry -lib-werror,
any.rts ==1.0.1, any.rts ==1.0.1,
any.safe ==0.3.19, any.safe ==0.3.19,
any.safe-exceptions ==0.1.7.2, any.safe-exceptions ==0.1.7.2,
any.scientific ==0.3.7.0, any.scientific ==0.3.7.0,
scientific -bytestring-builder -integer-simple, scientific -bytestring-builder -integer-simple,
any.semialign ==1.2.0.1, any.semigroupoids ==5.3.5,
semialign +semigroupoids,
any.semigroupoids ==5.3.7,
semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers, semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
any.setenv ==0.1.1.3, any.setenv ==0.1.1.3,
any.split ==0.2.3.4, any.split ==0.2.3.4,
any.splitmix ==0.1.0.4, any.splitmix ==0.1.0.3,
splitmix -optimised-mixer, splitmix -optimised-mixer,
any.stm ==2.5.0.1, any.stm ==2.5.0.1,
any.streamly ==0.8.2,
streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -streamly-core -use-c-malloc -use-unliftio,
any.strict ==0.4.0.1, any.strict ==0.4.0.1,
strict +assoc, strict +assoc,
any.strict-base ==0.4.0.0, any.strict-base ==0.4.0.0,
any.tagged ==0.8.6.1, any.tagged ==0.8.6.1,
tagged +deepseq +transformers, tagged +deepseq +transformers,
any.tagsoup ==0.14.8,
any.template-haskell ==2.16.0.0, any.template-haskell ==2.16.0.0,
any.temporary ==1.3, any.temporary ==1.3,
any.terminal-progress-bar ==0.4.1, any.terminal-progress-bar ==0.4.1,
any.terminal-size ==0.3.2.1, any.terminal-size ==0.3.2.1,
any.terminfo ==0.4.1.4, any.terminfo ==0.4.1.4,
any.text ==1.2.4.1, any.text ==1.2.4.1,
any.text-short ==0.1.5,
text-short -asserts,
any.text-zipper ==0.11, any.text-zipper ==0.11,
any.tf-random ==0.5, any.tf-random ==0.5,
any.th-abstraction ==0.4.3.0, any.th-abstraction ==0.4.3.0,
any.th-compat ==0.1.3, any.th-compat ==0.1.3,
any.th-lift ==0.8.2, any.th-lift ==0.8.2,
any.th-lift-instances ==0.1.19, any.th-lift-instances ==0.1.18,
any.these ==1.1.1.1, any.these ==1.1.1.1,
these +assoc, these +assoc,
any.time ==1.9.3, any.time ==1.9.3,
@@ -208,16 +187,14 @@ constraints: any.Cabal ==3.6.2.0,
any.transformers ==0.5.6.2, any.transformers ==0.5.6.2,
any.transformers-base ==0.4.6, any.transformers-base ==0.4.6,
transformers-base +orphaninstances, transformers-base +orphaninstances,
any.transformers-compat ==0.7.1, any.transformers-compat ==0.7,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.unicode-data ==0.3.0,
unicode-data -ucd2haskell,
any.unix ==2.7.2.2, any.unix ==2.7.2.2,
any.unix-bytestring ==0.3.7.6, any.unix-bytestring ==0.3.7.3,
any.unix-compat ==0.5.4, any.unix-compat ==0.5.3,
unix-compat -old-time, unix-compat -old-time,
any.unliftio-core ==0.2.0.1, any.unliftio-core ==0.2.0.1,
any.unordered-containers ==0.2.17.0, any.unordered-containers ==0.2.14.0,
unordered-containers -debug, unordered-containers -debug,
any.uri-bytestring ==0.3.3.1, any.uri-bytestring ==0.3.3.1,
uri-bytestring -lib-werror, uri-bytestring -lib-werror,
@@ -225,15 +202,12 @@ constraints: any.Cabal ==3.6.2.0,
any.uuid-types ==1.0.5, any.uuid-types ==1.0.5,
any.vector ==0.12.3.1, any.vector ==0.12.3.1,
vector +boundschecks -internalchecks -unsafechecks -wall, vector +boundschecks -internalchecks -unsafechecks -wall,
any.versions ==5.0.3, any.versions ==5.0.0,
any.vty ==5.33, any.vty ==5.33,
any.witherable ==0.4.2,
any.word-wrap ==0.5, any.word-wrap ==0.5,
any.word8 ==0.1.3, any.word8 ==0.1.3,
any.xor ==0.0.1.1, any.xor ==0.0.1.0,
any.yaml-streamly ==0.12.1,
yaml-streamly +no-examples +no-exe,
any.zlib ==0.6.2.3, any.zlib ==0.6.2.3,
zlib -bundled-c-zlib -non-blocking-ffi -pkg-config, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
any.zlib-bindings ==0.1.1.5 any.zlib-bindings ==0.1.1.5
index-state: hackage.haskell.org 2022-03-15T16:43:02Z index-state: hackage.haskell.org 2021-10-01T15:16:26Z

24
cabal.ghc901.project Normal file
View File

@@ -0,0 +1,24 @@
packages: ./ghcup.cabal
optional-packages: ./vendored/*/*.cabal
optimization: 2
package ghcup
tests: True
flags: +tui
constraints: http-io-streams -brotli
package libarchive
flags: -system-libarchive
package aeson-pretty
flags: +lib-only
package cabal-plan
flags: -exe
allow-newer: base, ghc-prim, template-haskell, language-c
with-compiler: ghc-9.0.1

View File

@@ -1,50 +1,49 @@
active-repositories: hackage.haskell.org:merge active-repositories: hackage.haskell.org:merge
constraints: any.Cabal ==3.6.2.0, constraints: any.Cabal ==3.4.0.0 || ==3.6.1.0,
Cabal -bundled-binary-generic, Cabal -bundled-binary-generic,
any.HUnit ==1.6.2.0, any.HUnit ==1.6.2.0,
any.HsOpenSSL ==0.11.7.2, any.HsOpenSSL ==0.11.7.2,
HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config, HsOpenSSL -fast-bignum -homebrew-openssl -macports-openssl -use-pkg-config,
any.OneTuple ==0.3.1, any.HsYAML ==0.2.1.0,
HsYAML -exe,
any.HsYAML-aeson ==0.2.0.0,
any.QuickCheck ==2.14.2, any.QuickCheck ==2.14.2,
QuickCheck -old-random +templatehaskell, QuickCheck -old-random +templatehaskell,
any.StateVar ==1.2.2, any.StateVar ==1.2.2,
any.abstract-deque ==0.3, any.aeson ==1.5.6.0,
abstract-deque -usecas, aeson -bytestring-builder -cffi -developer -fast,
any.aeson ==2.0.3.0, any.aeson-pretty ==0.8.8,
aeson -cffi +ordered-keymap,
any.aeson-pretty ==0.8.9,
aeson-pretty +lib-only, aeson-pretty +lib-only,
any.alex ==3.2.7.1, any.alex ==3.2.6,
any.ansi-terminal ==0.11.1, alex +small_base,
any.ansi-terminal ==0.11,
ansi-terminal -example, ansi-terminal -example,
any.ansi-wl-pprint ==0.6.9, any.ansi-wl-pprint ==0.6.9,
ansi-wl-pprint -example, ansi-wl-pprint -example,
any.array ==0.5.4.0, any.array ==0.5.4.0,
any.assoc ==1.0.2, any.assoc ==1.0.2,
any.async ==2.2.4, any.async ==2.2.3,
async -bench, async -bench,
any.atomic-primops ==0.8.4, any.attoparsec ==0.13.2.5,
atomic-primops -debug,
any.attoparsec ==0.14.4,
attoparsec -developer, attoparsec -developer,
any.base ==4.15.1.0, any.base ==4.15.0.0,
any.base-compat ==0.12.1, any.base-compat ==0.12.0,
any.base-compat-batteries ==0.12.1, any.base-compat-batteries ==0.12.0,
any.base-orphans ==0.8.6, any.base-orphans ==0.8.5,
any.base16-bytestring ==1.0.2.0, any.base16-bytestring ==1.0.1.0,
any.base64-bytestring ==1.2.1.0, any.base64-bytestring ==1.1.0.0,
any.bifunctors ==5.5.11, any.bifunctors ==5.5.11,
bifunctors +semigroups +tagged, bifunctors +semigroups +tagged,
any.binary ==0.8.8.0, any.binary ==0.8.8.0,
any.blaze-builder ==0.4.2.2, any.blaze-builder ==0.4.2.1,
any.brick ==0.64.2, any.brick ==0.64.1,
brick -demos, brick -demos,
any.bytestring ==0.10.12.1, any.bytestring ==0.10.12.1,
any.bz2 ==1.0.1.0, any.bz2 ==1.0.1.0,
bz2 -cross +with-bzlib, bz2 -cross +with-bzlib,
any.c2hs ==0.28.8, any.c2hs ==0.28.8,
c2hs +base3 -regression, c2hs +base3 -regression,
any.cabal-plan ==0.7.2.1, any.cabal-plan ==0.7.2.0,
cabal-plan -_ -exe -license-report, cabal-plan -_ -exe -license-report,
any.call-stack ==0.4.0, any.call-stack ==0.4.0,
any.case-insensitive ==1.2.1.0, any.case-insensitive ==1.2.1.0,
@@ -52,7 +51,7 @@ constraints: any.Cabal ==3.6.2.0,
any.chs-cabal ==0.1.1.1, any.chs-cabal ==0.1.1.1,
any.chs-deps ==0.1.0.0, any.chs-deps ==0.1.0.0,
chs-deps -cross, chs-deps -cross,
any.clock ==0.8.3, any.clock ==0.8.2,
clock -llvm, clock -llvm,
any.colour ==2.3.6, any.colour ==2.3.6,
any.comonad ==5.0.8, any.comonad ==5.0.8,
@@ -66,13 +65,13 @@ constraints: any.Cabal ==3.6.2.0,
contravariant +semigroups +statevar +tagged, contravariant +semigroups +statevar +tagged,
any.cpphs ==1.20.9.1, any.cpphs ==1.20.9.1,
cpphs -old-locale, cpphs -old-locale,
any.cryptohash-sha1 ==0.11.101.0, any.cryptohash-sha1 ==0.11.100.1,
any.cryptohash-sha256 ==0.11.102.1, any.cryptohash-sha256 ==0.11.102.0,
cryptohash-sha256 -exe +use-cbits, cryptohash-sha256 -exe +use-cbits,
any.data-clist ==0.2, any.data-clist ==0.1.2.3,
any.data-fix ==0.3.2, any.data-fix ==0.3.2,
any.deepseq ==1.4.5.0, any.deepseq ==1.4.5.0,
any.directory ==1.3.6.2, any.directory ==1.3.6.1,
any.disk-free-space ==0.1.0.1, any.disk-free-space ==0.1.0.1,
any.distributive ==0.6.2.1, any.distributive ==0.6.2.1,
distributive +semigroups +tagged, distributive +semigroups +tagged,
@@ -81,56 +80,45 @@ constraints: any.Cabal ==3.6.2.0,
any.exceptions ==0.10.4, any.exceptions ==0.10.4,
any.filepath ==1.4.2.1, any.filepath ==1.4.2.1,
any.free ==5.1.7, any.free ==5.1.7,
any.fusion-plugin-types ==0.1.0, any.generic-arbitrary ==0.1.0,
any.generic-arbitrary ==0.2.0, any.ghc-bignum ==1.0,
any.ghc ==9.0.2, any.ghc-boot-th ==9.0.1,
any.ghc-bignum ==1.1,
any.ghc-boot ==9.0.2,
any.ghc-boot-th ==9.0.2,
any.ghc-byteorder ==4.11.0.0.10, any.ghc-byteorder ==4.11.0.0.10,
any.ghc-heap ==9.0.2,
any.ghc-prim ==0.7.0, any.ghc-prim ==0.7.0,
any.ghci ==9.0.2,
any.happy ==1.20.0, any.happy ==1.20.0,
any.hashable ==1.4.0.2, any.hashable ==1.3.3.0,
hashable +containers +integer-gmp -random-initial-seed, hashable +integer-gmp -random-initial-seed,
any.haskus-utils-data ==1.4, any.haskus-utils-data ==1.4,
any.haskus-utils-types ==1.5.1, any.haskus-utils-types ==1.5.1,
any.haskus-utils-variant ==3.2.1, any.haskus-utils-variant ==3.1,
any.heaps ==0.4, any.hsc2hs ==0.68.7,
any.hpc ==0.6.1.0,
any.hsc2hs ==0.68.8,
hsc2hs -in-ghc-tree, hsc2hs -in-ghc-tree,
any.hspec ==2.9.4, any.hspec ==2.7.10,
any.hspec-core ==2.9.4, any.hspec-core ==2.7.10,
any.hspec-discover ==2.9.4, any.hspec-discover ==2.7.10,
any.hspec-expectations ==0.8.2, any.hspec-expectations ==0.8.2,
any.hspec-golden-aeson ==0.9.0.0, any.hspec-golden-aeson ==0.9.0.0,
any.http-io-streams ==0.1.6.0, any.http-io-streams ==0.1.6.0,
http-io-streams -brotli +fast-xor, http-io-streams -brotli +fast-xor,
any.indexed-profunctors ==0.1.1, any.indexed-profunctors ==0.1.1,
any.indexed-traversable ==0.1.2, any.indexed-traversable ==0.1.1,
any.indexed-traversable-instances ==0.1.1, any.indexed-traversable-instances ==0.1,
any.integer-logarithms ==1.0.3.1, any.integer-logarithms ==1.0.3.1,
integer-logarithms -check-bounds +integer-gmp, integer-logarithms -check-bounds +integer-gmp,
any.io-streams ==1.5.2.1, any.io-streams ==1.5.2.1,
io-streams +network -nointeractivetests +zlib, io-streams +network -nointeractivetests +zlib,
any.language-c ==0.9.0.1, any.language-c ==0.9.0.1,
language-c -allwarnings +iecfpextension +usebytestrings, language-c -allwarnings +iecfpextension +usebytestrings,
any.libarchive ==3.0.3.2, any.libarchive ==3.0.3.0,
libarchive -cross -low-memory +no-exe -system-libarchive, libarchive -cross -low-memory -system-libarchive,
any.libyaml-streamly ==0.2.1,
libyaml-streamly -no-unicode -system-libyaml,
any.lockfree-queue ==0.2.3.1,
any.lzma-static ==5.2.5.4, any.lzma-static ==5.2.5.4,
any.megaparsec ==9.2.0, any.megaparsec ==9.0.1,
megaparsec -dev, megaparsec -dev,
any.microlens ==0.4.12.0, any.microlens ==0.4.12.0,
any.microlens-mtl ==0.2.0.1, any.microlens-mtl ==0.2.0.1,
any.microlens-th ==0.4.3.10, any.microlens-th ==0.4.3.10,
any.monad-control ==1.0.3.1,
any.mtl ==2.2.2, any.mtl ==2.2.2,
any.network ==3.1.2.7, any.network ==3.1.2.2,
network -devel, network -devel,
any.network-uri ==2.6.4.1, any.network-uri ==2.6.4.1,
any.openssl-streams ==1.2.3.0, any.openssl-streams ==1.2.3.0,
@@ -139,9 +127,9 @@ constraints: any.Cabal ==3.6.2.0,
optics-core -explicit-generic-labels, optics-core -explicit-generic-labels,
any.optics-extra ==0.4, any.optics-extra ==0.4,
any.optics-th ==0.4, any.optics-th ==0.4,
any.optparse-applicative ==0.17.0.0, any.optparse-applicative ==0.16.1.0,
optparse-applicative +process, optparse-applicative +process,
any.os-release ==1.0.2.1, any.os-release ==1.0.2,
os-release -devel, os-release -devel,
any.parallel ==3.2.2.0, any.parallel ==3.2.2.0,
any.parsec ==3.1.14.0, any.parsec ==3.1.14.0,
@@ -150,56 +138,47 @@ constraints: any.Cabal ==3.6.2.0,
any.polyparse ==1.13, any.polyparse ==1.13,
any.pretty ==1.1.3.6, any.pretty ==1.1.3.6,
any.pretty-terminal ==0.1.0.0, any.pretty-terminal ==0.1.0.0,
any.primitive ==0.7.3.0, any.primitive ==0.7.2.0,
any.process ==1.6.13.2, any.process ==1.6.11.0,
any.profunctors ==5.6.2, any.profunctors ==5.6.2,
any.quickcheck-arbitrary-adt ==0.3.1.0, any.quickcheck-arbitrary-adt ==0.3.1.0,
any.quickcheck-io ==0.2.0, any.quickcheck-io ==0.2.0,
any.random ==1.2.1, any.random ==1.2.1,
any.recursion-schemes ==5.2.2.2, any.recursion-schemes ==5.2.2.1,
recursion-schemes +template-haskell, recursion-schemes +template-haskell,
any.regex-base ==0.94.0.2, any.regex-base ==0.94.0.1,
any.regex-posix ==0.96.0.1, any.regex-posix ==0.96.0.1,
regex-posix -_regex-posix-clib, regex-posix -_regex-posix-clib,
any.resourcet ==1.2.4.3, any.resourcet ==1.2.4.3,
any.retry ==0.8.1.2, any.rts ==1.0,
retry -lib-werror,
any.rts ==1.0.2,
any.safe ==0.3.19, any.safe ==0.3.19,
any.safe-exceptions ==0.1.7.2, any.safe-exceptions ==0.1.7.2,
any.scientific ==0.3.7.0, any.scientific ==0.3.7.0,
scientific -bytestring-builder -integer-simple, scientific -bytestring-builder -integer-simple,
any.semialign ==1.2.0.1, any.semigroupoids ==5.3.5,
semialign +semigroupoids,
any.semigroupoids ==5.3.7,
semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers, semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
any.setenv ==0.1.1.3, any.setenv ==0.1.1.3,
any.split ==0.2.3.4, any.split ==0.2.3.4,
any.splitmix ==0.1.0.4, any.splitmix ==0.1.0.3,
splitmix -optimised-mixer, splitmix -optimised-mixer,
any.stm ==2.5.0.0, any.stm ==2.5.0.0,
any.streamly ==0.8.2,
streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -streamly-core -use-c-malloc -use-unliftio,
any.strict ==0.4.0.1, any.strict ==0.4.0.1,
strict +assoc, strict +assoc,
any.strict-base ==0.4.0.0, any.strict-base ==0.4.0.0,
any.tagged ==0.8.6.1, any.tagged ==0.8.6.1,
tagged +deepseq +transformers, tagged +deepseq +transformers,
any.tagsoup ==0.14.8,
any.template-haskell ==2.17.0.0, any.template-haskell ==2.17.0.0,
any.temporary ==1.3, any.temporary ==1.3,
any.terminal-progress-bar ==0.4.1, any.terminal-progress-bar ==0.4.1,
any.terminal-size ==0.3.2.1, any.terminal-size ==0.3.2.1,
any.terminfo ==0.4.1.5, any.terminfo ==0.4.1.4,
any.text ==1.2.5.0, any.text ==1.2.4.1,
any.text-short ==0.1.5,
text-short -asserts,
any.text-zipper ==0.11, any.text-zipper ==0.11,
any.tf-random ==0.5, any.tf-random ==0.5,
any.th-abstraction ==0.4.3.0, any.th-abstraction ==0.4.3.0,
any.th-compat ==0.1.3, any.th-compat ==0.1.3,
any.th-lift ==0.8.2, any.th-lift ==0.8.2,
any.th-lift-instances ==0.1.19, any.th-lift-instances ==0.1.18,
any.these ==1.1.1.1, any.these ==1.1.1.1,
these +assoc, these +assoc,
any.time ==1.9.3, any.time ==1.9.3,
@@ -208,16 +187,14 @@ constraints: any.Cabal ==3.6.2.0,
any.transformers ==0.5.6.2, any.transformers ==0.5.6.2,
any.transformers-base ==0.4.6, any.transformers-base ==0.4.6,
transformers-base +orphaninstances, transformers-base +orphaninstances,
any.transformers-compat ==0.7.1, any.transformers-compat ==0.7,
transformers-compat -five +five-three -four +generic-deriving +mtl -three -two, transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
any.unicode-data ==0.3.0,
unicode-data -ucd2haskell,
any.unix ==2.7.2.2, any.unix ==2.7.2.2,
any.unix-bytestring ==0.3.7.6, any.unix-bytestring ==0.3.7.3,
any.unix-compat ==0.5.4, any.unix-compat ==0.5.3,
unix-compat -old-time, unix-compat -old-time,
any.unliftio-core ==0.2.0.1, any.unliftio-core ==0.2.0.1,
any.unordered-containers ==0.2.17.0, any.unordered-containers ==0.2.14.0,
unordered-containers -debug, unordered-containers -debug,
any.uri-bytestring ==0.3.3.1, any.uri-bytestring ==0.3.3.1,
uri-bytestring -lib-werror, uri-bytestring -lib-werror,
@@ -225,15 +202,12 @@ constraints: any.Cabal ==3.6.2.0,
any.uuid-types ==1.0.5, any.uuid-types ==1.0.5,
any.vector ==0.12.3.1, any.vector ==0.12.3.1,
vector +boundschecks -internalchecks -unsafechecks -wall, vector +boundschecks -internalchecks -unsafechecks -wall,
any.versions ==5.0.3, any.versions ==5.0.0,
any.vty ==5.33, any.vty ==5.33,
any.witherable ==0.4.2,
any.word-wrap ==0.5, any.word-wrap ==0.5,
any.word8 ==0.1.3, any.word8 ==0.1.3,
any.xor ==0.0.1.1, any.xor ==0.0.1.0,
any.yaml-streamly ==0.12.1,
yaml-streamly +no-examples +no-exe,
any.zlib ==0.6.2.3, any.zlib ==0.6.2.3,
zlib -bundled-c-zlib -non-blocking-ffi -pkg-config, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
any.zlib-bindings ==0.1.1.5 any.zlib-bindings ==0.1.1.5
index-state: hackage.haskell.org 2022-03-15T16:43:02Z index-state: hackage.haskell.org 2021-10-01T15:16:26Z

View File

@@ -1,34 +0,0 @@
packages: ./ghcup.cabal
optional-packages: ./vendored/*/*.cabal
optimization: 2
package ghcup
tests: True
flags: +tui
source-repository-package
type: git
location: https://github.com/bgamari/terminal-size.git
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
constraints: http-io-streams -brotli,
any.Cabal ==3.6.2.0,
any.aeson >= 2.0.1.0
package libarchive
flags: -system-libarchive
package aeson-pretty
flags: +lib-only
package cabal-plan
flags: -exe
package aeson
flags: +ordered-keymap
allow-newer: base, ghc-prim, template-haskell, language-c
with-compiler: ghc-9.0.2

View File

@@ -8,16 +8,19 @@ package ghcup
tests: True tests: True
flags: +tui flags: +tui
source-repository-package
type: git
location: https://github.com/bgamari/terminal-size.git
tag: 34ea816bd63f75f800eedac12c6908c6f3736036
constraints: http-io-streams -brotli, constraints: http-io-streams -brotli,
any.Cabal ==3.6.2.0, any.Cabal ==3.4.0.0 || ==3.6.2.0,
any.aeson >= 2.0.1.0, any.aeson >= 2.0.1.0
-- https://github.com/typeable/generic-arbitrary/issues/14
any.generic-arbitrary < 0.2.1 source-repository-package
type: git
location: https://github.com/hasufell/aeson-pretty.git
tag: e902ab866bb41d990b66af3644aeb352ff7aaf6f
source-repository-package
type: git
location: https://github.com/hasufell/HsYAML-aeson.git
tag: b4b4ab8592918b52a9f2e5bb0c5a795b9721b4f3
package libarchive package libarchive
flags: -system-libarchive flags: -system-libarchive
@@ -31,7 +34,4 @@ package cabal-plan
package aeson package aeson
flags: +ordered-keymap flags: +ordered-keymap
package streamly
flags: +use-unliftio
allow-newer: base, ghc-prim, template-haskell, language-c allow-newer: base, ghc-prim, template-haskell, language-c

View File

@@ -1,7 +0,0 @@
#include "dirutils.h"
unsigned int
__posixdir_d_type(struct dirent* d)
{
return(d -> d_type);
}

View File

@@ -1,15 +0,0 @@
#ifndef POSIXPATHS_CBITS_DIRUTILS_H
#define POSIXPATHS_CBITS_DIRUTILS_H
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
extern unsigned int
__posixdir_d_type(struct dirent* d)
;
#endif

View File

@@ -36,10 +36,6 @@ key-bindings:
show-all-tools: show-all-tools:
KChar: 't' KChar: 't'
# The caching for the metadata files containing download info, depending on last access time
# of the file. These usually are in '~/.ghcup/cache/ghcup-<ver>.yaml'.
meta-cache: 300 # in seconds
# Where to get GHC/cabal/hls download info/versions from. For more detailed explanation # Where to get GHC/cabal/hls download info/versions from. For more detailed explanation
# check the 'URLSource' type in the code. # check the 'URLSource' type in the code.
url-source: url-source:
@@ -48,16 +44,12 @@ url-source:
## Example 1: Read download info from this location instead ## Example 1: Read download info from this location instead
## Accepts file/http/https scheme ## Accepts file/http/https scheme
## Can also be an array of URLs or an array of 'Either GHCupInfo URL', in
## which case they are merged right-biased (overwriting duplicate versions).
# OwnSource: "file:///home/jule/git/ghcup-hs/ghcup-0.0.3.yaml" # OwnSource: "file:///home/jule/git/ghcup-hs/ghcup-0.0.3.yaml"
## Example 2: Add custom tarballs to the default downloads, overwriting duplicate versions. ## Example 2: Add custom tarballs to the default downloads, overwriting duplicate versions
## Can also be an array of 'Either GHCupInfo URL', also see Example 3.
# AddSource: # AddSource:
# Left: # Left:
# globalTools: {} # toolRequirements: {} # this is ignored
# toolRequirements: {}
# ghcupDownloads: # ghcupDownloads:
# GHC: # GHC:
# 9.10.2: # 9.10.2:
@@ -70,8 +62,6 @@ url-source:
# dlSubdir: ghc-7.10.3 # dlSubdir: ghc-7.10.3
# dlHash: 01cfbad8dff1e8b34a5fdca8caeaf843b56e36af919e29cd68870d2588563db5 # dlHash: 01cfbad8dff1e8b34a5fdca8caeaf843b56e36af919e29cd68870d2588563db5
## Example 3: Add multiple custom download files to the default downloads via right-biased merge (overwriting duplicate ## Example 3: Add a custom download file to the default downloads, overwriting duplicate versions
## versions).
# AddSource: # AddSource:
# - Right: "file:///home/jule/git/ghcup-hs/ghcup-prereleases.yaml" # Right: "file:///home/jule/git/ghcup-hs/ghcup-custom.yaml"
# - Right: "file:///home/jule/git/ghcup-hs/ghcup-custom.yaml"

Submodule data/metadata deleted from 7d8f7eaf66

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,5 @@
:root { :root {
--theme-purple: #5E5184; --theme-purple: #5E5184;
--theme-purple-dark: rgba(69, 59, 97, 0.5);
--ukraine-top: #0057B8;
--ukraine-bottom: #FFD700;
--link-pink: #9E358F; --link-pink: #9E358F;
} }
@@ -111,12 +108,12 @@ body.homepage>div.container div.col-md-9 {
.bg-primary { .bg-primary {
background-image: none; background-image: none;
background-color: var(--ukraine-top) !important; background-color: var(--theme-purple) !important;
} }
body .bg-primary { body .bg-primary {
background-image: none; background-image: none;
background-color: var(--ukraine-top); background-color: var(--theme-purple);
border: 0px; border: 0px;
} }
@@ -128,8 +125,8 @@ body .btn-primary {
.navbar.fixed-top { .navbar.fixed-top {
background-image: none; background-image: none;
background-color: var(--ukraine-top); background-color: var(--theme-purple);
border-bottom: 40px solid var(--ukraine-bottom); border-bottom: 5px solid rgba(69, 59, 97, 0.5);
padding: 0px; padding: 0px;
} }

View File

@@ -12,7 +12,8 @@ organised tree-ish in `GHCup.Utils` and `GHCup.Utils.*`.
Anything dealing with ghcup specific directories is in Anything dealing with ghcup specific directories is in
`GHCup.Utils.Dirs`. `GHCup.Utils.Dirs`.
Download information on where to fetch bindists from is in the [ghcup-metadata](https://github.com/haskell/ghcup-metadata) repository. Download information on where to fetch bindists from is in the appropriate
yaml files: `data/metadata/ghcup-<yaml-ver>.yaml`.
## Design decisions ## Design decisions
@@ -65,14 +66,18 @@ Some light suggestions:
### Adding a new GHC version ### Adding a new GHC version
Head over to: [https://github.com/haskell/ghcup-metadata#adding-a-new-ghc-version](https://github.com/haskell/ghcup-metadata#adding-a-new-ghc-version) 1. open the latest `data/metadata/ghcup-<yaml-ver>.yaml`
2. find the latest ghc version (in yaml tree e.g. `ghcupDownloads -> GHC -> 8.10.7`)
3. copy-paste it
4. adjust the version, tags, changelog, source url
5. adjust the various bindist urls (make sure to also change the yaml anchors)
6. run `cabal run exe:ghcup-gen -- check -f data/metadata/ghcup-<yaml-ver>.yaml`
7. run `cabal run exe:ghcup-gen -- check-tarballs -f data/metadata/ghcup-<yaml-ver>.yaml -u 'ghc-8\.10\.8'`
### Adding a new CLI command ### Adding a new CLI command
An example illustration on how to deal with [optparse-applicative](https://hackage.haskell.org/package/optparse-applicative) can be seen here: [https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26](https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26) An example illustration on how to deal with [optparse-applicative](https://hackage.haskell.org/package/optparse-applicative) can be seen here: [https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26](https://gitlab.haskell.org/haskell/ghcup-hs/-/commit/c19dd5ee8b2edbaf0336af143f1c75b6f4843e26)
Every subcommand now lives in its own module under [GHCup.OptParse.MyCommand](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/app/ghcup/GHCup/OptParse).
## Major refactors ## Major refactors
1. First major refactor included adding cross support. This added 1. First major refactor included adding cross support. This added
@@ -84,37 +89,30 @@ Every subcommand now lives in its own module under [GHCup.OptParse.MyCommand](ht
The major changes here were switching `hpath` library out for `filepath`/`directory` (sadly) and The major changes here were switching `hpath` library out for `filepath`/`directory` (sadly) and
introducing a non-unix way of handling processes via the `process` library. It also introduced considerable introducing a non-unix way of handling processes via the `process` library. It also introduced considerable
amounts of CPP wrt file handling, installation etc. amounts of CPP wrt file handling, installation etc.
3. This refactor split up the huge `Main.hs` and put every subcommand in its own module: [#212](https://gitlab.haskell.org/haskell/ghcup-hs/-/merge_requests/212)
# Releasing # Releasing
1. Update version in `ghcup.cabal` 1. Update version in `ghcup.cabal` and `boostrap-haskell` (`ghver` variable at the top of the script)
2. Update `GHCup.Version` module. `ghcupURL` must only be updated if we change the `GHCupInfo` type or the YAML representation of it. The version of the YAML represents the change increments. `ghcUpVer` is the current application version, read from `ghcup.cabal`. 2. Update `GHCup.Version` module. `ghcupURL` must only be updated if we change the `GHCupInfo` type or the YAML representation of it. The version of the YAML represents the change increments. `ghcUpVer` is the current application version, read from `ghcup.cabal`.
3. Add ChangeLog entry 3. Add ChangeLog entry
4. If a new ghcup yaml version is needed, create one at [ghcup-metadata repo](https://github.com/haskell/ghcup-metadata) and push to a temporary release branch, then update the `data/metadata` submodule in ghcup-hs repo to that branch, so CI can pass 4. Add/fix downloads in `ghcup-<ver>.yaml` (under `data/metadata`), then verify with `ghcup-gen check -f data/metadata/ghcup-<ver>.yaml` and possibly (example only) `ghcup-gen check-tarballs -f data/metadata/ghcup-<ver>.yaml -u 'ghc-8.10.7'`. Generally, new GHC/cabal/stack/hls versions are only added to the latest yaml file. New GHCup versions are added to all (great care must be taken here to not break the parser... e.g. ARM platforms don't parse in all older formats).
5. Commit and git push with tag. Wait for tests to succeed and release artifacts to build. 5. Commit and git push with tag. Wait for tests to succeed and release artifacts to build.
6. Download release artifacts and upload them `downloads.haskell.org/~ghcup` along with checksum files (also check `scripts/releasing/pull_release_artifacts.sh` and `scripts/releasing/sftp-upload-artifacts.sh`) 6. Download release artifacts and upload them `downloads.haskell.org/~ghcup` along with checksum files (`sha256sum --tag * > SHA256SUMS && gpg --detach-sign -u <your-email> SHA256SUMS`)
7. Add ghcup release artifacts to ALL yaml files, see [ghcup-metadata repo](https://github.com/haskell/ghcup-metadata) 7. Add ghcup release artifacts to ALL yaml files (see point 4.)
8. Upload the final `ghcup-<ver>.yaml` (and a detached GPG sig of it) to `webhost.haskell.org/ghcup/data/` (for yaml versions <= 0.0.6) as well as [https://github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata) (for all versions). 8. Upload the final `data/metadata/ghcup-<ver>.yaml` (and a detached GPG sig of it) to `webhost.haskell.org/ghcup/data/`.
9. Update version in `scripts/bootstrap/bootstrap-haskell` (`ghver` variable at the top of the script) 9. Update `bootstrap-haskell` and `bootstrap-haskell.ps1` to `webhost.haskell.org/ghcup/sh/`
10. Upload `scripts/bootstrap/bootstrap-haskell` and `scripts/bootstrap/bootstrap-haskell.ps1` to `webhost.haskell.org/ghcup/sh/` 10. Update the top-level ghcup symlinks at `downloads.haskell.org/~ghcup`
11. Update the top-level ghcup symlinks at `downloads.haskell.org/~ghcup` (see `scripts/releasing/sftp-symlink-artifacts.sh`) 11. Post on reddit/discourse/etc. and collect rewards
12. Update the `data/metadata` submodule in ghcup-hs repo to master
13. Do hackage release
14. Post on reddit/discourse/etc. and collect rewards
# Documentation # Documentation

View File

@@ -1,6 +1,6 @@
# User Guide # User Guide
This is a more in-depth guide specific to GHCup. `ghcup --help` is your friend. `ghcup --help` is your friend.
## Basic usage ## Basic usage
@@ -32,16 +32,12 @@ ghcup install cabal
ghcup upgrade ghcup upgrade
``` ```
### Tags and shortcuts ## Configuration
GHCup has a number of tags and version shortcuts, that can be used as arguments to **install**/**set** etc. A configuration file can be put in `~/.ghcup/config.yaml`. The default config file
All of the following are valid arguments to `ghcup install ghc`: explaining all possible configurations can be found in this repo: [config.yaml](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/data/config.yaml).
* `latest`, `recommended` Partial configuration is fine. Command line options always override the config file settings.
* `base-4.15.1.0`
* `9.0.2`, `9.0`, `9`
If the argument is omitted, the default is `recommended`.
## Manpages ## Manpages
@@ -57,152 +53,6 @@ as e.g. `/etc/bash_completion.d/ghcup` (depending on distro)
and make sure your bashrc sources the startup script and make sure your bashrc sources the startup script
(`/usr/share/bash-completion/bash_completion` on some distros). (`/usr/share/bash-completion/bash_completion` on some distros).
## Portability
`ghcup` is very portable. There are a few exceptions though:
1. `ghcup tui` is only available on non-windows platforms
2. legacy subcommands `ghcup install` (without a tool identifier) and `ghcup install-cabal` may be removed in the future
# Configuration
A configuration file can be put in `~/.ghcup/config.yaml`. The default config file
explaining all possible configurations can be found in this repo: [config.yaml](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/data/config.yaml).
Partial configuration is fine. Command line options always override the config file settings.
## Env variables
This is the complete list of env variables that change GHCup behavior:
* `GHCUP_USE_XDG_DIRS`: see [XDG support](#xdg-support) above
* `TMPDIR`: where ghcup does the work (unpacking, building, ...)
* `GHCUP_INSTALL_BASE_PREFIX`: the base of ghcup (default: `$HOME`)
* `GHCUP_CURL_OPTS`: additional options that can be passed to curl
* `GHCUP_WGET_OPTS`: additional options that can be passed to wget
* `GHCUP_GPG_OPTS`: additional options that can be passed to gpg
* `GHCUP_SKIP_UPDATE_CHECK`: Skip the (possibly annoying) update check when you run a command
* `CC`/`LD` etc.: full environment is passed to the build system when compiling GHC via GHCup
### XDG support
To enable XDG style directories, set the environment variable `GHCUP_USE_XDG_DIRS` to anything.
Then you can control the locations via XDG environment variables as such:
* `XDG_DATA_HOME`: GHCs will be unpacked in `ghcup/ghc` subdir (default: `~/.local/share`)
* `XDG_CACHE_HOME`: logs and download files will be stored in `ghcup` subdir (default: `~/.cache`)
* `XDG_BIN_HOME`: binaries end up here (default: `~/.local/bin`)
* `XDG_CONFIG_HOME`: the config file is stored in `ghcup` subdir as `config.yaml` (default: `~/.config`)
**Note that `ghcup` makes some assumptions about structure of files in `XDG_BIN_HOME`. So if you have other tools
installing e.g. stack/cabal/ghc into it, this will likely clash. In that case consider disabling XDG support.**
## Caching
GHCup has a few caching mechanisms to avoid redownloads. All cached files end up in `~/.ghcup/cache` by default.
### Downloads cache
Downloaded tarballs (such as GHC, cabal, etc.) are not cached by default unless you pass `ghcup --cache` or set caching
in your [config](#configuration) via `ghcup config set cache true`.
### Metadata cache
The metadata files (also see [github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata))
have a 5 minutes cache per default depending on the last access time of the file. That means if you run
`ghcup list` 10 times in a row, only the first time will trigger a download attempt.
### Clearing the cache
If you experience problems, consider clearing the cache via `ghcup gc --cache`.
## Metadata
The metadata are the files that describe tool versions, where to download them etc. and
can be viewed here: [https://github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata)
### Mirrors
GHCup allows to use custom mirrors/download-info hosted by yourself or 3rd parties.
To use a mirror, set the following option in `~/.ghcup/config.yaml`:
```yml
url-source:
# Accepts file/http/https scheme
OwnSource: "https://some-url/ghcup-0.0.6.yaml"
```
See [config.yaml](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/data/config.yaml)
for more options.
Alternatively you can do it via a cli switch:
```sh
ghcup --url-source=https://some-url/ghcup-0.0.6.yaml list
```
#### Known mirrors
1. [https://mirror.sjtu.edu.cn/docs/ghcup](https://mirror.sjtu.edu.cn/docs/ghcup)
### (Pre-)Release channels
A release channel is basically just a metadata file location. You can add additional release
channels that complement the default one, such as the **prerelease channel** like so:
```sh
ghcup config add-release-channel https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml
```
This will result in `~/.ghcup/config.yaml` to contain this record:
```yml
url-source:
AddSource:
- Right: https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml
```
You can add as many channels as you like. They are combined under *Last*, so versions from the prerelease channel
here overwrite the default ones, if any.
To remove the channel, delete the entire `url-source` section or set it back to the default:
```yml
url-source:
GHCupURL: []
```
If you want to combine your release channel with a mirror, you'd do it like so:
```yml
url-source:
OwnSource:
# base metadata
- "https://mirror.sjtu.edu.cn/ghcup/yaml/ghcup/data/ghcup-0.0.6.yaml"
# prerelease channel
- "https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-prereleases-0.0.7.yaml"
```
# More on installation
## Installing custom bindists
There are a couple of good use cases to install custom bindists:
1. manually built bindists (e.g. with patches)
- example: `ghcup install ghc -u 'file:///home/mearwald/tmp/ghc-eff-patches/ghc-8.10.2-x86_64-deb10-linux.tar.xz' 8.10.2-eff`
2. GHC head CI bindists
- example: `ghcup install ghc -u 'https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27' head`
3. DWARF bindists
- example: `ghcup install ghc -u 'https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-deb10-linux-dwarf.tar.xz' 8.10.2-dwarf`
Since the version parser is pretty lax, `8.10.2-eff` and `head` are both valid versions
and produce the binaries `ghc-8.10.2-eff` and `ghc-head` respectively.
GHCup always needs to know which version the bindist corresponds to (this is not automatically
detected).
## Compiling GHC from source ## Compiling GHC from source
Compiling from source is supported for both source tarballs and arbitrary git refs. See `ghcup compile ghc --help` Compiling from source is supported for both source tarballs and arbitrary git refs. See `ghcup compile ghc --help`
@@ -226,9 +76,50 @@ For distributions with non-standard locations of cross toolchain and
libraries, this may need some tweaking of `build.mk` or configure args. libraries, this may need some tweaking of `build.mk` or configure args.
See `ghcup compile ghc --help` for further information. See `ghcup compile ghc --help` for further information.
## Isolated installs ## XDG support
**Before using isolated installs, make sure to have at least GHCup version 0.1.17.8!** To enable XDG style directories, set the environment variable `GHCUP_USE_XDG_DIRS` to anything.
Then you can control the locations via XDG environment variables as such:
* `XDG_DATA_HOME`: GHCs will be unpacked in `ghcup/ghc` subdir (default: `~/.local/share`)
* `XDG_CACHE_HOME`: logs and download files will be stored in `ghcup` subdir (default: `~/.cache`)
* `XDG_BIN_HOME`: binaries end up here (default: `~/.local/bin`)
* `XDG_CONFIG_HOME`: the config file is stored in `ghcup` subdir as `config.yaml` (default: `~/.config`)
**Note that `ghcup` makes some assumptions about structure of files in `XDG_BIN_HOME`. So if you have other tools
installing e.g. stack/cabal/ghc into it, this will likely clash. In that case consider disabling XDG support.**
## Env variables
This is the complete list of env variables that change GHCup behavior:
* `GHCUP_USE_XDG_DIRS`: see [XDG support](#xdg-support) above
* `TMPDIR`: where ghcup does the work (unpacking, building, ...)
* `GHCUP_INSTALL_BASE_PREFIX`: the base of ghcup (default: `$HOME`)
* `GHCUP_CURL_OPTS`: additional options that can be passed to curl
* `GHCUP_WGET_OPTS`: additional options that can be passed to wget
* `GHCUP_GPG_OPTS`: additional options that can be passed to gpg
* `GHCUP_SKIP_UPDATE_CHECK`: Skip the (possibly annoying) update check when you run a command
* `CC`/`LD` etc.: full environment is passed to the build system when compiling GHC via GHCup
## Installing custom bindists
There are a couple of good use cases to install custom bindists:
1. manually built bindists (e.g. with patches)
- example: `ghcup install ghc -u 'file:///home/mearwald/tmp/ghc-eff-patches/ghc-8.10.2-x86_64-deb10-linux.tar.xz' 8.10.2-eff`
2. GHC head CI bindists
- example: `ghcup install ghc -u 'https://gitlab.haskell.org/api/v4/projects/1/jobs/artifacts/master/raw/ghc-x86_64-fedora27-linux.tar.xz?job=validate-x86_64-linux-fedora27' head`
3. DWARF bindists
- example: `ghcup install ghc -u 'https://downloads.haskell.org/~ghc/8.10.2/ghc-8.10.2-x86_64-deb10-linux-dwarf.tar.xz' 8.10.2-dwarf`
Since the version parser is pretty lax, `8.10.2-eff` and `head` are both valid versions
and produce the binaries `ghc-8.10.2-eff` and `ghc-head` respectively.
GHCup always needs to know which version the bindist corresponds to (this is not automatically
detected).
## Isolated installs
Ghcup also enables you to install a tool (GHC, Cabal, HLS, Stack) at an isolated location of your choosing. Ghcup also enables you to install a tool (GHC, Cabal, HLS, Stack) at an isolated location of your choosing.
These installs, as the name suggests, are separate from your main installs and DO NOT conflict with them. These installs, as the name suggests, are separate from your main installs and DO NOT conflict with them.
@@ -278,10 +169,66 @@ For the full list of env variables and parameters to tweak the script behavior,
* [bootstrap-haskell for linux/darwin/freebsd](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell#L7) * [bootstrap-haskell for linux/darwin/freebsd](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell#L7)
* [bootstrap-haskell.ps1 for windows](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell.ps1#L17) * [bootstrap-haskell.ps1 for windows](https://gitlab.haskell.org/haskell/ghcup-hs/-/blob/master/scripts/bootstrap/bootstrap-haskell.ps1#L17)
### github workflows ### Example github workflow
On github workflows you can use [https://github.com/haskell/actions/](https://github.com/haskell/actions/). On github workflows you can use [https://github.com/haskell/actions/](https://github.com/haskell/actions/)
GHCup itself is also pre-installed on all platforms, but may use non-standard install locations.
If you want to install ghcup manually though, here's an example config:
```yml
name: Haskell CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build-cabal:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
ghc: ['8.10.7', '9.0.1']
cabal: ['3.4.0.0']
steps:
- uses: actions/checkout@v2
- if: matrix.os == 'windows-latest'
name: Install ghcup on windows
run: Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $false,$true,$true,$false,$false,$false,$false,"C:\"
- if: matrix.os == 'windows-latest'
name: Add ghcup to PATH
run: echo "/c/ghcup/bin" >> $GITHUB_PATH
shell: bash
- if: matrix.os != 'windows-latest'
name: Install ghcup on non-windows
run: curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 sh
- name: Install ghc/cabal
run: |
ghcup install ghc ${{ matrix.ghc }}
ghcup install cabal ${{ matrix.cabal }}
shell: bash
- name: Update cabal index
run: cabal update
shell: bash
- name: Build
run: cabal build --enable-tests --enable-benchmarks
shell: bash
- name: Run tests
run: cabal test
shell: bash
```
## GPG verification ## GPG verification
@@ -311,16 +258,50 @@ gpg-setting: GPGLax # GPGStrict | GPGLax | GPGNone
In `GPGStrict` mode, ghcup will fail if verification fails. In `GPGLax` mode it will just print a warning. In `GPGStrict` mode, ghcup will fail if verification fails. In `GPGLax` mode it will just print a warning.
You can also pass the mode via `ghcup --gpg <strict|lax|none>`. You can also pass the mode via `ghcup --gpg <strict|lax|none>`.
# Tips and tricks ## Tips and tricks
## ghcup run ### with_ghc wrapper (e.g. for HLS)
If you don't want to explicitly switch the active GHC all the time and are using Due to some HLS [bugs](https://github.com/mpickering/hie-bios/issues/194) it's necessary that the `ghc` in PATH
tools that rely on the plain `ghc` binary, GHCup provides an easy way to execute is the one defined in `cabal.project`. With some simple shell functions, we can start our editor with the appropriate
commands with a certain toolchain prepended to PATH, e.g.: path prepended.
For bash, in e.g. `~/.bashrc` define:
```sh ```sh
ghcup run --ghc 8.10.7 --cabal latest --hls latest --stack latest --install -- code Setup.hs with_ghc() {
local np=$(ghcup --offline whereis -d ghc $1 || { ghcup --cache install ghc $1 && ghcup whereis -d ghc $1 ;})
if [ -e "${np}" ] ; then
shift
PATH="$np:$PATH" "$@"
else
>&2 echo "Cannot find or install GHC version $1"
return 1
fi
}
``` ```
This will execute vscode with GHC set to 8.10.7 and all other tools to their latest version. For fish shell, in e.g. `~/.config/fish/config.fish` define:
```fish
function with_ghc
set --local np (ghcup --offline whereis -d ghc $argv[1] ; or begin ghcup --cache install ghc $argv[1] ; and ghcup whereis -d ghc $argv[1] ; end)
if test -e "$np"
PATH="$np:$PATH" $argv[2..-1]
else
echo "Cannot find or install GHC version $argv[1]" 1>&2
return 1
end
end
```
Then start a new shell and issue:
```sh
# replace 'code' with your editor
with_ghc 8.10.5 code path/to/haskell/source
```
Cabal and HLS will now see `8.10.5` as the primary GHC, without the need to
run `ghcup set` all the time when switching between projects.

View File

@@ -17,7 +17,6 @@ hide:
<div class="text-center main-buttons"> <div class="text-center main-buttons">
<a href="install/" class="btn btn-primary" role="button">Getting Started</a> <a href="install/" class="btn btn-primary" role="button">Getting Started</a>
<a href="steps/" class="btn btn-primary" role="button">First steps</a>
<a href="guide/" class="btn btn-primary" role="button">User Guide</a> <a href="guide/" class="btn btn-primary" role="button">User Guide</a>
</div> </div>

View File

@@ -2,7 +2,7 @@
GHCup makes it easy to install specific versions of GHC on GNU/Linux, GHCup makes it easy to install specific versions of GHC on GNU/Linux,
macOS (aka Darwin), FreeBSD and Windows and can also bootstrap a fresh [Haskell developer environment](./install/#supported-tools) from scratch. macOS (aka Darwin), FreeBSD and Windows and can also bootstrap a fresh [Haskell developer environment](./install/#supported-tools) from scratch.
It follows the UNIX philosophy of [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well). 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). 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). 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).
## Installation ## Installation
@@ -22,113 +22,28 @@ For Windows, run this in a PowerShell session:
Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true Set-ExecutionPolicy Bypass -Scope Process -Force;[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -ArgumentList $true
``` ```
There's also a [youtube video](https://www.youtube.com/watch?v=bB4fmQiUYPw) explaining installation on windows.
If you want to know what these scripts do, check out the [source code at the repository](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/scripts/bootstrap). Advanced users may want to perform a [manual installation](#manual-install) and GPG verify the binaries. If you want to know what these scripts do, check out the [source code at the repository](https://gitlab.haskell.org/haskell/ghcup-hs/-/tree/master/scripts/bootstrap). Advanced users may want to perform a [manual installation](#manual-install) and GPG verify the binaries.
### Which versions get installed? ## First steps
GHCup has two main channels for every tool: **recommended** and **latest**. By default, it installs *recommended*. 1. To get started with creating a Haskell project, follow the [Getting Started with Haskell and Cabal](https://cabal.readthedocs.io/en/latest/getting-started.html) guide
2. To properly learn Haskell, run through the [CIS 194 Haskell course](https://www.cis.upenn.edu/~cis194/spring13/) including exercises
*latest* follows the latest release of every tool, while *recommended* is at the discretion of the GHCup maintainers and based on community adoption (hackage libraries, tools like HLS, stackage support, etc.) and known bugs. 3. To learn more about Haskell Toolchain management, check out the [ghcup user guide](./guide.md)
Also see [tags and shortcuts](../guide/#tags-and-shortcuts) for more information.
## Next steps
1. Follow the [First steps guide](../steps) on how to build a "Hello world" program, use `ghc`, run an interactive REPL and create a Haskell project
2. To understand the difference and overlap of `stack` and `cabal`, read on [here](https://gist.github.com/merijn/8152d561fb8b011f9313c48d876ceb07)
3. To learn Haskell proper check out the links at [How to learn Haskell proper](../steps#how-to-learn-haskell-proper)
4. To learn more about Haskell Toolchain management, check out the [ghcup user guide](./guide.md)
## Uninstallation ## Uninstallation
On linux, just run `ghcup nuke`, then make sure any ghcup added lines in your `~/.bashrc` (or similar) are removed. On linux, just run `ghcup nuke`, then make sure any ghcup added lines in your `~/.bashrc` (or similar) are removed.
On windows, right click on the `Uninstall Haskell.ps1` PowerShell script on your Desktop and select *Run with PowerShell*. On windows, double-click on the `Uninstall Haskell.ps1` PowerShell script on your Desktop.
## Supported tools ## Supported tools
GHCup supports the following tools, which are also known as the **Haskell Toolchain**: GHCup supports the following tools, which are also known as the **Haskell Toolchain**:
<details> <summary>Show all supported <a href='https://www.haskell.org/ghc/'>GHC</a> versions</summary> 1. [GHC](https://www.haskell.org/ghc/)
<table> 2. [cabal-install](https://cabal.readthedocs.io/en/latest/)
<thead><tr><th>GHC Version</th><th>Tags</th></tr></thead> 3. [haskell-language-server](https://haskell-language-server.readthedocs.io/en/latest/)
<tbody> 4. [stack](https://docs.haskellstack.org/en/latest/README/)
<tr><td>9.2.2</td><td><span style="color:blue">latest</span>, base-4.16.1.0</td></tr>
<tr><td>9.2.1</td><td>base-4.16.0.0</td></tr>
<tr><td>9.0.2</td><td>base-4.15.1.0</td></tr>
<tr><td>9.0.1</td><td>base-4.15.0.0</td></tr>
<tr><td>8.10.7</td><td><span style="color:green">recommended</span>, base-4.14.3.0</td></tr>
<tr><td>8.10.6</td><td>base-4.14.3.0</td></tr>
<tr><td>8.10.5</td><td>base-4.14.2.0</td></tr>
<tr><td>8.10.4</td><td>base-4.14.1.0</td></tr>
<tr><td>8.10.3</td><td>base-4.14.1.0</td></tr>
<tr><td>8.10.2</td><td>base-4.14.1.0</td></tr>
<tr><td>8.10.1</td><td>base-4.14.0.0</td></tr>
<tr><td>8.8.4</td><td>base-4.13.0.0</td></tr>
<tr><td>8.8.3</td><td>base-4.13.0.0</td></tr>
<tr><td>8.8.2</td><td>base-4.13.0.0</td></tr>
<tr><td>8.8.1</td><td>base-4.13.0.0</td></tr>
<tr><td>8.6.5</td><td>base-4.12.0.0</td></tr>
<tr><td>8.6.4</td><td>base-4.12.0.0</td></tr>
<tr><td>8.6.3</td><td>base-4.12.0.0</td></tr>
<tr><td>8.6.2</td><td>base-4.12.0.0</td></tr>
<tr><td>8.6.1</td><td>base-4.12.0.0</td></tr>
<tr><td>8.4.4</td><td>base-4.11.1.0</td></tr>
<tr><td>8.4.3</td><td>base-4.11.1.0</td></tr>
<tr><td>8.4.2</td><td>base-4.11.1.0</td></tr>
<tr><td>8.4.1</td><td>base-4.11.0.0</td></tr>
<tr><td>8.2.2</td><td>base-4.10.1.0</td></tr>
<tr><td>8.0.2</td><td>base-4.9.1.0</td></tr>
<tr><td>7.10.3</td><td>base-4.8.2.0</td></tr>
</tbody>
</table>
</details>
<details> <summary>Show all supported <a href='https://cabal.readthedocs.io/en/stable/'>cabal-install</a> versions</summary>
<table>
<thead><tr><th>Cabal Version</th><th>Tags</th></tr></thead>
<tbody>
<tr><td>3.6.2.0</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
<tr><td>3.6.0.0</td><td></td></tr>
<tr><td>3.4.1.0</td><td></td></tr>
<tr><td>3.4.0.0</td><td></td></tr>
<tr><td>3.2.0.0</td><td></td></tr>
<tr><td>3.0.0.0</td><td></td></tr>
<tr><td>2.4.1.0</td><td></td></tr>
</tbody>
</table>
</details>
<details> <summary>Show all supported <a href='https://haskell-language-server.readthedocs.io/en/stable/'>HLS</a> versions</summary>
<table>
<thead><tr><th>HLS Version</th><th>Tags</th></tr></thead>
<tbody>
<tr><td>1.7.0.0</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
<tr><td>1.6.1.0</td><td></td></tr>
<tr><td>1.6.0.0</td><td></td></tr>
<tr><td>1.5.1</td><td></td></tr>
<tr><td>1.5.0</td><td></td></tr>
<tr><td>1.4.0</td><td></td></tr>
<tr><td>1.3.0</td><td></td></tr>
<tr><td>1.2.0</td><td></td></tr>
<tr><td>1.1.0</td><td></td></tr>
</tbody>
</table>
</details>
<details> <summary>Show all supported <a href='https://docs.haskellstack.org/en/stable/README/'>Stack</a> versions</summary>
<table>
<thead><tr><th>Stack Version</th><th>Tags</th></tr></thead>
<tbody>
<tr><td>2.7.5</td><td><span style="color:blue">latest</span>, <span style="color:green">recommended</span></td></tr>
<tr><td>2.7.3</td><td></td></tr>
<tr><td>2.7.1</td><td></td></tr>
<tr><td>2.5.1</td><td></td></tr>
</tbody>
</table>
</details>
## Supported platforms ## Supported platforms
@@ -163,15 +78,14 @@ May or may not work, several issues:
Unsupported. GHC may or may not work. Upgrade to WSL2. Unsupported. GHC may or may not work. Upgrade to WSL2.
### MacOS <10.13 ### MacOS <13
Not supported. Would require separate binaries, since >=10.13 binaries are incompatible. Not supported. Would require separate binaries, since >=13 binaries are incompatible.
Please upgrade. Please upgrade.
### MacOS aarch64 ### MacOS aarch64
HLS bindists are still experimental. Stack has only unofficial binaries for this platform. HLS bindists are still experimental. Stack is theoretically supported, but has no binaries yet.
There are various issues with GHC itself.
### FreeBSD ### FreeBSD
@@ -180,7 +94,7 @@ HLS bindists are experimental.
### Linux ARMv7/AARCH64 ### Linux ARMv7/AARCH64
Lower availability of bindists. Stack and HLS binaries are experimental. Lower availability of bindists. HLS only has experimental ones. Stack not supported currently.
## Manual install ## Manual install

View File

@@ -4,638 +4,363 @@
<!-- Generated by graphviz version 2.44.0 (0) <!-- Generated by graphviz version 2.44.0 (0)
--> -->
<!-- Title: G Pages: 1 --> <!-- Title: G Pages: 1 -->
<svg width="720pt" height="648pt" <svg width="719pt" height="648pt"
viewBox="0.00 0.00 719.60 648.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 719.28 648.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(0.45 0.45) rotate(0) translate(4 1421.5)"> <g id="graph0" class="graph" transform="scale(0.81 0.81) rotate(0) translate(4 794.2)">
<title>G</title> <title>G</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-1421.5 1579,-1421.5 1579,4 -4,4"/> <polygon fill="white" stroke="transparent" points="-4,4 -4,-794.2 882,-794.2 882,4 -4,4"/>
<g id="clust1" class="cluster"> <g id="clust1" class="cluster">
<title>cluster_0</title> <title>cluster_0</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="8,-12.99 8,-1346.06 1567,-1346.06 1567,-12.99 8,-12.99"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="8,-8.95 8,-660.37 870,-660.37 870,-8.95 8,-8.95"/>
<text text-anchor="middle" x="787.5" y="-1330.86" font-family="Times-Roman" font-size="14.00">GHCup</text> <text text-anchor="middle" x="439" y="-645.17" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g> </g>
<g id="clust2" class="cluster"> <g id="clust2" class="cluster">
<title>cluster_1</title> <title>cluster_1</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="1102,-529.33 1102,-823.22 1362,-823.22 1362,-529.33 1102,-529.33"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="190,-364.88 190,-541.72 450,-541.72 450,-364.88 190,-364.88"/>
<text text-anchor="middle" x="1232" y="-808.02" font-family="Times-Roman" font-size="14.00">Download</text> <text text-anchor="middle" x="320" y="-526.52" font-family="Times-Roman" font-size="14.00">Download</text>
</g> </g>
<g id="clust3" class="cluster"> <g id="clust3" class="cluster">
<title>cluster_2</title> <title>cluster_2</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="16,-940.13 16,-1295.72 1559,-1295.72 1559,-940.13 16,-940.13"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="271,-191.39 271,-355.93 452,-355.93 452,-191.39 271,-191.39"/>
<text text-anchor="middle" x="787.5" y="-1280.52" font-family="Times-Roman" font-size="14.00">OptParse</text> <text text-anchor="middle" x="361.5" y="-340.73" font-family="Times-Roman" font-size="14.00">Types</text>
</g> </g>
<g id="clust4" class="cluster"> <g id="clust4" class="cluster">
<title>cluster_3</title> <title>cluster_3</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="1196,-277.65 1196,-516.34 1377,-516.34 1377,-277.65 1196,-277.65"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="566,-17.91 566,-576.42 862,-576.42 862,-17.91 566,-17.91"/>
<text text-anchor="middle" x="1286.5" y="-501.14" font-family="Times-Roman" font-size="14.00">Types</text> <text text-anchor="middle" x="714" y="-561.22" font-family="Times-Roman" font-size="14.00">Utils</text>
</g> </g>
<g id="clust5" class="cluster"> <g id="clust5" class="cluster">
<title>cluster_4</title> <title>cluster_4</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="586,-25.98 586,-701.44 1082,-701.44 1082,-25.98 586,-25.98"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="640,-271.98 640,-448.82 758,-448.82 758,-271.98 640,-271.98"/>
<text text-anchor="middle" x="834" y="-686.24" font-family="Times-Roman" font-size="14.00">Utils</text> <text text-anchor="middle" x="699" y="-433.62" font-family="Times-Roman" font-size="14.00">File</text>
</g> </g>
<g id="clust6" class="cluster"> <g id="clust6" class="cluster">
<title>cluster_5</title> <title>cluster_5</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="744,-394.56 744,-651.11 862,-651.11 862,-394.56 744,-394.56"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="583,-26.86 583,-110.81 653,-110.81 653,-26.86 583,-26.86"/>
<text text-anchor="middle" x="803" y="-635.91" font-family="Times-Roman" font-size="14.00">File</text> <text text-anchor="middle" x="618" y="-95.61" font-family="Times-Roman" font-size="14.00">String</text>
</g> </g>
<g id="clust7" class="cluster"> <g id="clust7" class="cluster">
<title>cluster_6</title> <title>cluster_6</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="995,-38.97 995,-160.75 1065,-160.75 1065,-38.97 995,-38.97"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="712,-457.78 712,-541.72 782,-541.72 782,-457.78 712,-457.78"/>
<text text-anchor="middle" x="1030" y="-145.55" font-family="Times-Roman" font-size="14.00">String</text> <text text-anchor="middle" x="747" y="-526.52" font-family="Times-Roman" font-size="14.00">Version</text>
</g>
<g id="clust8" class="cluster">
<title>cluster_7</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="594,-529.33 594,-651.11 664,-651.11 664,-529.33 594,-529.33"/>
<text text-anchor="middle" x="629" y="-635.91" font-family="Times-Roman" font-size="14.00">Version</text>
</g> </g>
<!-- u1 --> <!-- u1 -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>u1</title> <title>u1</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="1165" cy="-743.99" rx="55.49" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="387" cy="-486.95" rx="55.49" ry="18"/>
<text text-anchor="middle" x="1165" y="-740.29" font-family="Times-Roman" font-size="14.00">Download</text> <text text-anchor="middle" x="387" y="-483.25" font-family="Times-Roman" font-size="14.00">Download</text>
</g> </g>
<!-- u12 --> <!-- u12 -->
<g id="node24" class="node"> <g id="node7" class="node">
<title>u12</title> <title>u12</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="2" cx="825" cy="-571.99" rx="27" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="2" cx="688" cy="-393.95" rx="27" ry="18"/>
<text text-anchor="middle" x="825" y="-568.29" font-family="Times-Roman" font-size="14.00">File</text> <text text-anchor="middle" x="688" y="-390.25" font-family="Times-Roman" font-size="14.00">File</text>
</g> </g>
<!-- u1&#45;&gt;u12 --> <!-- u1&#45;&gt;u12 -->
<g id="edge13" class="edge"> <g id="edge15" class="edge">
<title>u1&#45;&gt;u12</title> <title>u1&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M1125,-731.45C1042,-707.49 858.55,-654.33 856,-651.99 841.25,-638.47 833.42,-617.06 829.33,-599.94"/> <path fill="none" stroke="black" d="M416.08,-471.48C425.43,-467.42 435.96,-463.44 446,-460.95 491.68,-449.63 616.67,-467.2 657,-442.95 665.62,-437.77 672.27,-429.17 677.16,-420.67"/>
<polygon fill="black" stroke="black" points="832.71,-599.01 827.23,-589.94 825.86,-600.45 832.71,-599.01"/> <polygon fill="black" stroke="black" points="680.34,-422.15 681.79,-411.65 674.11,-418.95 680.34,-422.15"/>
</g> </g>
<!-- u11 --> <!-- u11 -->
<g id="node32" class="node"> <g id="node15" class="node">
<title>u11</title> <title>u11</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1045" cy="-571.99" rx="28.7" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="603" cy="-393.95" rx="28.7" ry="18"/>
<text text-anchor="middle" x="1045" y="-568.29" font-family="Times-Roman" font-size="14.00">Dirs</text> <text text-anchor="middle" x="603" y="-390.25" font-family="Times-Roman" font-size="14.00">Dirs</text>
</g> </g>
<!-- u1&#45;&gt;u11 --> <!-- u1&#45;&gt;u11 -->
<g id="edge12" class="edge"> <g id="edge14" class="edge">
<title>u1&#45;&gt;u11</title> <title>u1&#45;&gt;u11</title>
<path fill="none" stroke="black" d="M1152.43,-726.38C1138.8,-708.25 1116.51,-678.31 1098,-651.99 1085.29,-633.92 1071.4,-613.17 1061.02,-597.47"/> <path fill="none" stroke="black" d="M419.14,-472.31C427.86,-468.62 437.29,-464.63 446,-460.95 489.08,-442.77 538.85,-421.87 570.6,-408.54"/>
<polygon fill="black" stroke="black" points="1063.78,-595.29 1055.36,-588.87 1057.94,-599.14 1063.78,-595.29"/> <polygon fill="black" stroke="black" points="571.98,-411.76 579.85,-404.66 569.27,-405.3 571.98,-411.76"/>
</g> </g>
<!-- u13 --> <!-- u13 -->
<g id="node35" class="node"> <g id="node18" class="node">
<title>u13</title> <title>u13</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1414" cy="-571.99" rx="44.39" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="109" cy="-300.95" rx="44.39" ry="18"/>
<text text-anchor="middle" x="1414" y="-568.29" font-family="Times-Roman" font-size="14.00">Version</text> <text text-anchor="middle" x="109" y="-297.25" font-family="Times-Roman" font-size="14.00">Version</text>
</g> </g>
<!-- u1&#45;&gt;u13 --> <!-- u1&#45;&gt;u13 -->
<g id="edge11" class="edge"> <g id="edge13" class="edge">
<title>u1&#45;&gt;u13</title> <title>u1&#45;&gt;u13</title>
<path fill="none" stroke="black" d="M1206.8,-732.08C1250.56,-718.96 1319.26,-693.39 1366,-651.99 1382.37,-637.49 1394.91,-616.04 1403.06,-599.16"/> <path fill="none" stroke="black" d="M356.68,-471.88C346.23,-467.63 334.3,-463.44 323,-460.95 290.82,-453.87 49.58,-466.95 27,-442.95 4.16,-418.68 12.6,-398.02 27,-367.95 36.61,-347.88 55.63,-331.97 72.94,-320.81"/>
<polygon fill="black" stroke="black" points="1406.35,-600.37 1407.35,-589.82 1399.99,-597.45 1406.35,-600.37"/> <polygon fill="black" stroke="black" points="75.07,-323.61 81.77,-315.4 71.41,-317.64 75.07,-323.61"/>
</g> </g>
<!-- u17 --> <!-- u17 -->
<g id="node2" class="node"> <g id="node2" class="node">
<title>u17</title> <title>u17</title>
<ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="1267" cy="-571.99" rx="30.59" ry="18"/> <ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="332" cy="-393.95" rx="30.59" ry="18"/>
<text text-anchor="middle" x="1267" y="-568.29" font-family="Times-Roman" font-size="14.00">Utils</text> <text text-anchor="middle" x="332" y="-390.25" font-family="Times-Roman" font-size="14.00">Utils</text>
</g> </g>
<!-- u5 --> <!-- u5 -->
<g id="node23" class="node"> <g id="node6" class="node">
<title>u5</title> <title>u5</title>
<ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="1238" cy="-436.99" rx="33.6" ry="18"/> <ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="410" cy="-300.95" rx="33.6" ry="18"/>
<text text-anchor="middle" x="1238" y="-433.29" font-family="Times-Roman" font-size="14.00">JSON</text> <text text-anchor="middle" x="410" y="-297.25" font-family="Times-Roman" font-size="14.00">JSON</text>
</g> </g>
<!-- u17&#45;&gt;u5 --> <!-- u17&#45;&gt;u5 -->
<g id="edge14" class="edge"> <g id="edge16" class="edge">
<title>u17&#45;&gt;u5</title> <title>u17&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1263.27,-553.87C1258.35,-531.33 1249.65,-491.44 1243.86,-464.85"/> <path fill="none" stroke="black" d="M345.23,-377.52C357.37,-363.35 375.54,-342.16 389.55,-325.81"/>
<polygon fill="black" stroke="black" points="1247.27,-464.06 1241.72,-455.04 1240.43,-465.56 1247.27,-464.06"/> <polygon fill="black" stroke="black" points="392.59,-327.64 396.44,-317.77 387.28,-323.09 392.59,-327.64"/>
</g> </g>
<!-- u19 --> <!-- u19 -->
<g id="node3" class="node"> <g id="node3" class="node">
<title>u19</title> <title>u19</title>
<ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="1296" cy="-743.99" rx="57.69" ry="18"/> <ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="256" cy="-486.95" rx="57.69" ry="18"/>
<text text-anchor="middle" x="1296" y="-740.29" font-family="Times-Roman" font-size="14.00">IOStreams</text> <text text-anchor="middle" x="256" y="-483.25" font-family="Times-Roman" font-size="14.00">IOStreams</text>
</g> </g>
<!-- u19&#45;&gt;u17 --> <!-- u19&#45;&gt;u17 -->
<g id="edge15" class="edge">
<title>u19&#45;&gt;u17</title>
<path fill="none" stroke="black" d="M1293.06,-725.75C1288.01,-696.15 1277.65,-635.42 1271.63,-600.1"/>
<polygon fill="black" stroke="black" points="1275.04,-599.29 1269.9,-590.02 1268.14,-600.46 1275.04,-599.29"/>
</g>
<!-- u21 -->
<g id="node4" class="node">
<title>u21</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="742" cy="-1215.99" rx="51.99" ry="18"/>
<text text-anchor="middle" x="742" y="-1212.29" font-family="Times-Roman" font-size="14.00">OptParse</text>
</g>
<!-- u23 -->
<g id="node6" class="node">
<title>u23</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="846" cy="-1098.99" rx="38.19" ry="18"/>
<text text-anchor="middle" x="846" y="-1095.29" font-family="Times-Roman" font-size="14.00">Install</text>
</g>
<!-- u21&#45;&gt;u23 -->
<g id="edge16" class="edge">
<title>u21&#45;&gt;u23</title>
<path fill="none" stroke="black" d="M756.9,-1198.51C774.61,-1178.93 804.29,-1146.11 824.49,-1123.77"/>
<polygon fill="black" stroke="black" points="827.35,-1125.83 831.46,-1116.07 822.16,-1121.13 827.35,-1125.83"/>
</g>
<!-- u24 -->
<g id="node7" class="node">
<title>u24</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="929" cy="-1098.99" rx="27" ry="18"/>
<text text-anchor="middle" x="929" y="-1095.29" font-family="Times-Roman" font-size="14.00">Set</text>
</g>
<!-- u21&#45;&gt;u24 -->
<g id="edge17" class="edge"> <g id="edge17" class="edge">
<title>u21&#45;&gt;u24</title> <title>u19&#45;&gt;u17</title>
<path fill="none" stroke="black" d="M766.45,-1199.96C801.13,-1178.63 864.45,-1139.69 900.98,-1117.22"/> <path fill="none" stroke="black" d="M272.49,-469.68C280.19,-461.83 289.33,-452.14 297,-442.95 303.18,-435.56 309.51,-427.17 315.06,-419.49"/>
<polygon fill="black" stroke="black" points="902.97,-1120.1 909.66,-1111.88 899.31,-1114.14 902.97,-1120.1"/> <polygon fill="black" stroke="black" points="318.21,-421.09 321.16,-410.91 312.51,-417.03 318.21,-421.09"/>
</g>
<!-- u25 -->
<g id="node8" class="node">
<title>u25</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1197" cy="-1098.99" rx="38.19" ry="18"/>
<text text-anchor="middle" x="1197" y="-1095.29" font-family="Times-Roman" font-size="14.00">UnSet</text>
</g>
<!-- u21&#45;&gt;u25 -->
<g id="edge18" class="edge">
<title>u21&#45;&gt;u25</title>
<path fill="none" stroke="black" d="M785.46,-1206.08C860.33,-1190.39 1018.18,-1155.79 1149,-1116.99 1152.09,-1116.07 1155.28,-1115.07 1158.47,-1114.03"/>
<polygon fill="black" stroke="black" points="1159.62,-1117.34 1167.97,-1110.82 1157.37,-1110.7 1159.62,-1117.34"/>
</g>
<!-- u26 -->
<g id="node9" class="node">
<title>u26</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1001" cy="-1098.99" rx="27" ry="18"/>
<text text-anchor="middle" x="1001" y="-1095.29" font-family="Times-Roman" font-size="14.00">Rm</text>
</g>
<!-- u21&#45;&gt;u26 -->
<g id="edge19" class="edge">
<title>u21&#45;&gt;u26</title>
<path fill="none" stroke="black" d="M773.51,-1201.46C816.81,-1182.8 897.08,-1147.94 965,-1116.99 966.79,-1116.17 968.63,-1115.33 970.49,-1114.47"/>
<polygon fill="black" stroke="black" points="972.15,-1117.56 979.72,-1110.15 969.18,-1111.21 972.15,-1117.56"/>
</g>
<!-- u27 -->
<g id="node10" class="node">
<title>u27</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1093" cy="-1098.99" rx="47.39" ry="18"/>
<text text-anchor="middle" x="1093" y="-1095.29" font-family="Times-Roman" font-size="14.00">Compile</text>
</g>
<!-- u21&#45;&gt;u27 -->
<g id="edge20" class="edge">
<title>u21&#45;&gt;u27</title>
<path fill="none" stroke="black" d="M778.57,-1203.01C843.42,-1181.76 978.33,-1137.56 1048.47,-1114.58"/>
<polygon fill="black" stroke="black" points="1049.92,-1117.79 1058.34,-1111.35 1047.74,-1111.13 1049.92,-1117.79"/>
</g>
<!-- u28 -->
<g id="node11" class="node">
<title>u28</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="64" cy="-1098.99" rx="40.09" ry="18"/>
<text text-anchor="middle" x="64" y="-1095.29" font-family="Times-Roman" font-size="14.00">Config</text>
</g>
<!-- u21&#45;&gt;u28 -->
<g id="edge21" class="edge">
<title>u21&#45;&gt;u28</title>
<path fill="none" stroke="black" d="M692.47,-1210.33C585.48,-1199.59 325.33,-1169.91 113,-1116.99 109.85,-1116.21 106.62,-1115.31 103.4,-1114.34"/>
<polygon fill="black" stroke="black" points="104.39,-1110.98 93.8,-1111.28 102.27,-1117.65 104.39,-1110.98"/>
</g>
<!-- u29 -->
<g id="node12" class="node">
<title>u29</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="415" cy="-1098.99" rx="46.59" ry="18"/>
<text text-anchor="middle" x="415" y="-1095.29" font-family="Times-Roman" font-size="14.00">Whereis</text>
</g>
<!-- u21&#45;&gt;u29 -->
<g id="edge22" class="edge">
<title>u21&#45;&gt;u29</title>
<path fill="none" stroke="black" d="M706.69,-1202.57C646.37,-1181.36 523.67,-1138.21 458.29,-1115.21"/>
<polygon fill="black" stroke="black" points="459.28,-1111.85 448.69,-1111.84 456.96,-1118.46 459.28,-1111.85"/>
</g>
<!-- u30 -->
<g id="node13" class="node">
<title>u30</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="507" cy="-1098.99" rx="27" ry="18"/>
<text text-anchor="middle" x="507" y="-1095.29" font-family="Times-Roman" font-size="14.00">List</text>
</g>
<!-- u21&#45;&gt;u30 -->
<g id="edge23" class="edge">
<title>u21&#45;&gt;u30</title>
<path fill="none" stroke="black" d="M713.18,-1200.89C668.44,-1178.99 582.69,-1137.03 537.15,-1114.75"/>
<polygon fill="black" stroke="black" points="538.6,-1111.55 528.07,-1110.3 535.52,-1117.84 538.6,-1111.55"/>
</g>
<!-- u31 -->
<g id="node14" class="node">
<title>u31</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1303" cy="-1098.99" rx="50.09" ry="18"/>
<text text-anchor="middle" x="1303" y="-1095.29" font-family="Times-Roman" font-size="14.00">Upgrade</text>
</g>
<!-- u21&#45;&gt;u31 -->
<g id="edge24" class="edge">
<title>u21&#45;&gt;u31</title>
<path fill="none" stroke="black" d="M787.97,-1207.41C876.68,-1192.47 1077.4,-1157.17 1244,-1116.99 1248.02,-1116.02 1252.18,-1114.95 1256.34,-1113.84"/>
<polygon fill="black" stroke="black" points="1257.28,-1117.21 1265.99,-1111.19 1255.42,-1110.46 1257.28,-1117.21"/>
</g>
<!-- u32 -->
<g id="node15" class="node">
<title>u32</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="614" cy="-1098.99" rx="61.99" ry="18"/>
<text text-anchor="middle" x="614" y="-1095.29" font-family="Times-Roman" font-size="14.00">ChangeLog</text>
</g>
<!-- u21&#45;&gt;u32 -->
<g id="edge25" class="edge">
<title>u21&#45;&gt;u32</title>
<path fill="none" stroke="black" d="M724.2,-1199C702.26,-1179.29 664.8,-1145.63 639.73,-1123.11"/>
<polygon fill="black" stroke="black" points="641.84,-1120.29 632.06,-1116.21 637.16,-1125.5 641.84,-1120.29"/>
</g>
<!-- u33 -->
<g id="node16" class="node">
<title>u33</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="742" cy="-1098.99" rx="48.19" ry="18"/>
<text text-anchor="middle" x="742" y="-1095.29" font-family="Times-Roman" font-size="14.00">Prefetch</text>
</g>
<!-- u21&#45;&gt;u33 -->
<g id="edge26" class="edge">
<title>u21&#45;&gt;u33</title>
<path fill="none" stroke="black" d="M742,-1197.52C742,-1178.93 742,-1149.23 742,-1127.49"/>
<polygon fill="black" stroke="black" points="745.5,-1127.24 742,-1117.24 738.5,-1127.24 745.5,-1127.24"/>
</g>
<!-- u34 -->
<g id="node17" class="node">
<title>u34</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="235" cy="-1098.99" rx="27" ry="18"/>
<text text-anchor="middle" x="235" y="-1095.29" font-family="Times-Roman" font-size="14.00">GC</text>
</g>
<!-- u21&#45;&gt;u34 -->
<g id="edge27" class="edge">
<title>u21&#45;&gt;u34</title>
<path fill="none" stroke="black" d="M694.23,-1208.86C608.04,-1196.96 421.48,-1167.38 271,-1116.99 269.08,-1116.35 267.13,-1115.63 265.19,-1114.86"/>
<polygon fill="black" stroke="black" points="266.16,-1111.47 255.6,-1110.73 263.4,-1117.9 266.16,-1111.47"/>
</g>
<!-- u35 -->
<g id="node18" class="node">
<title>u35</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="315" cy="-1098.99" rx="35.19" ry="18"/>
<text text-anchor="middle" x="315" y="-1095.29" font-family="Times-Roman" font-size="14.00">DInfo</text>
</g>
<!-- u21&#45;&gt;u35 -->
<g id="edge28" class="edge">
<title>u21&#45;&gt;u35</title>
<path fill="none" stroke="black" d="M699.03,-1205.74C627.68,-1189.99 480.59,-1155.89 359,-1116.99 356.46,-1116.18 353.84,-1115.29 351.23,-1114.37"/>
<polygon fill="black" stroke="black" points="352.41,-1111.08 341.81,-1110.93 350,-1117.65 352.41,-1111.08"/>
</g>
<!-- u36 -->
<g id="node19" class="node">
<title>u36</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1461" cy="-1098.99" rx="90.18" ry="18"/>
<text text-anchor="middle" x="1461" y="-1095.29" font-family="Times-Roman" font-size="14.00">ToolRequirements</text>
</g>
<!-- u21&#45;&gt;u36 -->
<g id="edge29" class="edge">
<title>u21&#45;&gt;u36</title>
<path fill="none" stroke="black" d="M788.7,-1207.83C892.07,-1191.92 1148.03,-1152.27 1362,-1116.99 1369.56,-1115.74 1377.44,-1114.42 1385.32,-1113.09"/>
<polygon fill="black" stroke="black" points="1385.98,-1116.53 1395.25,-1111.41 1384.81,-1109.63 1385.98,-1116.53"/>
</g>
<!-- u37 -->
<g id="node20" class="node">
<title>u37</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="156" cy="-1098.99" rx="33.6" ry="18"/>
<text text-anchor="middle" x="156" y="-1095.29" font-family="Times-Roman" font-size="14.00">Nuke</text>
</g>
<!-- u21&#45;&gt;u37 -->
<g id="edge30" class="edge">
<title>u21&#45;&gt;u37</title>
<path fill="none" stroke="black" d="M693.33,-1209.49C597.41,-1197.93 377.73,-1167.91 199,-1116.99 196.41,-1116.25 193.76,-1115.42 191.12,-1114.52"/>
<polygon fill="black" stroke="black" points="192.23,-1111.2 181.64,-1111.08 189.84,-1117.78 192.23,-1111.2"/>
</g>
<!-- u22 -->
<g id="node5" class="node">
<title>u22</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="742" cy="-981.99" rx="51.19" ry="18"/>
<text text-anchor="middle" x="742" y="-978.29" font-family="Times-Roman" font-size="14.00">Common</text>
</g>
<!-- u0 -->
<g id="node33" class="node">
<title>u0</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="2" cx="705" cy="-864.99" rx="42.49" ry="18"/>
<text text-anchor="middle" x="705" y="-861.29" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g>
<!-- u22&#45;&gt;u0 -->
<g id="edge31" class="edge">
<title>u22&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M736.54,-964.02C730.5,-945.25 720.69,-914.76 713.61,-892.76"/>
<polygon fill="black" stroke="black" points="716.88,-891.48 710.48,-883.03 710.21,-893.62 716.88,-891.48"/>
</g>
<!-- u23&#45;&gt;u22 -->
<g id="edge32" class="edge">
<title>u23&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M831.54,-1082C813.97,-1062.57 784.14,-1029.59 763.78,-1007.08"/>
<polygon fill="black" stroke="black" points="766.31,-1004.66 757.01,-999.59 761.12,-1009.35 766.31,-1004.66"/>
</g>
<!-- u24&#45;&gt;u22 -->
<g id="edge33" class="edge">
<title>u24&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M909.69,-1086.12C877.99,-1066.62 814.52,-1027.58 775.4,-1003.53"/>
<polygon fill="black" stroke="black" points="776.84,-1000.31 766.49,-998.05 773.18,-1006.27 776.84,-1000.31"/>
</g>
<!-- u25&#45;&gt;u0 -->
<g id="edge34" class="edge">
<title>u25&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M1170.9,-1085.68C1088.88,-1047.01 836.24,-927.88 741.44,-883.17"/>
<polygon fill="black" stroke="black" points="742.78,-879.94 732.24,-878.84 739.8,-886.27 742.78,-879.94"/>
</g>
<!-- u26&#45;&gt;u22 -->
<g id="edge35" class="edge">
<title>u26&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M979.72,-1087.83C974.89,-1085.55 969.78,-1083.17 965,-1080.99 901.54,-1052.07 827.31,-1019.75 782.51,-1000.4"/>
<polygon fill="black" stroke="black" points="783.71,-997.11 773.14,-996.36 780.94,-1003.53 783.71,-997.11"/>
</g>
<!-- u27&#45;&gt;u22 -->
<g id="edge36" class="edge">
<title>u27&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M1058.38,-1086.65C994.71,-1065.79 859,-1021.32 787.79,-997.99"/>
<polygon fill="black" stroke="black" points="788.78,-994.63 778.19,-994.85 786.6,-1001.29 788.78,-994.63"/>
</g>
<!-- u15 -->
<g id="node28" class="node">
<title>u15</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="705" cy="-571.99" rx="30.59" ry="18"/>
<text text-anchor="middle" x="705" y="-568.29" font-family="Times-Roman" font-size="14.00">Utils</text>
</g>
<!-- u28&#45;&gt;u15 -->
<g id="edge37" class="edge">
<title>u28&#45;&gt;u15</title>
<path fill="none" stroke="black" d="M67.99,-1080.83C82.87,-1020.89 141.11,-819.44 271,-725.99 416.7,-621.17 526.05,-761.83 668,-651.99 684.52,-639.21 693.98,-617.26 699.19,-599.74"/>
<polygon fill="black" stroke="black" points="702.58,-600.61 701.79,-590.04 695.82,-598.8 702.58,-600.61"/>
</g>
<!-- u29&#45;&gt;u22 -->
<g id="edge38" class="edge">
<title>u29&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M448.46,-1086.22C507.6,-1065.42 630.64,-1022.15 697.09,-998.78"/>
<polygon fill="black" stroke="black" points="698.59,-1001.97 706.86,-995.35 696.27,-995.36 698.59,-1001.97"/>
</g>
<!-- u30&#45;&gt;u22 -->
<g id="edge39" class="edge">
<title>u30&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M528.27,-1087.58C568.07,-1068.1 654.29,-1025.91 704.21,-1001.48"/>
<polygon fill="black" stroke="black" points="705.94,-1004.53 713.38,-996.99 702.86,-998.25 705.94,-1004.53"/>
</g>
<!-- u31&#45;&gt;u0 -->
<g id="edge40" class="edge">
<title>u31&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M1271.29,-1084.99C1209.35,-1059.66 1067.86,-1002.15 948,-955.99 877.11,-928.69 794.01,-898.24 745.65,-880.68"/>
<polygon fill="black" stroke="black" points="746.76,-877.36 736.17,-877.24 744.37,-883.94 746.76,-877.36"/>
</g>
<!-- u32&#45;&gt;u22 -->
<g id="edge41" class="edge">
<title>u32&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M632.07,-1081.76C654.08,-1061.98 691.42,-1028.44 716.39,-1006"/>
<polygon fill="black" stroke="black" points="718.92,-1008.43 724.02,-999.14 714.25,-1003.22 718.92,-1008.43"/>
</g>
<!-- u33&#45;&gt;u22 -->
<g id="edge42" class="edge">
<title>u33&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M742,-1080.52C742,-1061.93 742,-1032.23 742,-1010.49"/>
<polygon fill="black" stroke="black" points="745.5,-1010.24 742,-1000.24 738.5,-1010.24 745.5,-1010.24"/>
</g>
<!-- u34&#45;&gt;u0 -->
<g id="edge43" class="edge">
<title>u34&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M253.73,-1086.02C294.34,-1060.34 393.93,-998.82 482,-955.99 543.91,-925.88 618.59,-897.22 663.9,-880.63"/>
<polygon fill="black" stroke="black" points="665.18,-883.89 673.38,-877.18 662.79,-877.31 665.18,-883.89"/>
</g>
<!-- u35&#45;&gt;u0 -->
<g id="edge44" class="edge">
<title>u35&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M337.09,-1084.85C402.43,-1045.98 595.06,-931.39 672.36,-885.41"/>
<polygon fill="black" stroke="black" points="674.39,-888.27 681.2,-880.15 670.81,-882.25 674.39,-888.27"/>
</g>
<!-- u14 -->
<g id="node36" class="node">
<title>u14</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="934" cy="-743.99" rx="48.19" ry="18"/>
<text text-anchor="middle" x="934" y="-740.29" font-family="Times-Roman" font-size="14.00">Platform</text>
</g>
<!-- u36&#45;&gt;u14 -->
<g id="edge45" class="edge">
<title>u36&#45;&gt;u14</title>
<path fill="none" stroke="black" d="M1436.29,-1081.44C1349.76,-1023.48 1060.5,-829.72 964.94,-765.71"/>
<polygon fill="black" stroke="black" points="966.82,-762.76 956.56,-760.1 962.93,-768.58 966.82,-762.76"/>
</g>
<!-- u18 -->
<g id="node37" class="node">
<title>u18</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1461" cy="-743.99" rx="73.39" ry="18"/>
<text text-anchor="middle" x="1461" y="-740.29" font-family="Times-Roman" font-size="14.00">Requirements</text>
</g>
<!-- u36&#45;&gt;u18 -->
<g id="edge46" class="edge">
<title>u36&#45;&gt;u18</title>
<path fill="none" stroke="black" d="M1461,-1080.96C1461,-1024.36 1461,-842.13 1461,-772.44"/>
<polygon fill="black" stroke="black" points="1464.5,-772.26 1461,-762.26 1457.5,-772.26 1464.5,-772.26"/>
</g>
<!-- u37&#45;&gt;u0 -->
<g id="edge47" class="edge">
<title>u37&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M175.44,-1084.01C214.9,-1056.2 307.95,-993.37 394,-955.99 483.86,-916.95 595.52,-889.35 657.64,-875.71"/>
<polygon fill="black" stroke="black" points="658.52,-879.1 667.55,-873.56 657.03,-872.26 658.52,-879.1"/>
</g> </g>
<!-- u3 --> <!-- u3 -->
<g id="node21" class="node"> <g id="node4" class="node">
<title>u3</title> <title>u3</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="1257" cy="-319.99" rx="36.29" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="399" cy="-220.95" rx="36.29" ry="18"/>
<text text-anchor="middle" x="1257" y="-316.29" font-family="Times-Roman" font-size="14.00">Types</text> <text text-anchor="middle" x="399" y="-217.25" font-family="Times-Roman" font-size="14.00">Types</text>
</g> </g>
<!-- u4 --> <!-- u4 -->
<g id="node22" class="node"> <g id="node5" class="node">
<title>u4</title> <title>u4</title>
<ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="1329" cy="-436.99" rx="39.79" ry="18"/> <ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="319" cy="-300.95" rx="39.79" ry="18"/>
<text text-anchor="middle" x="1329" y="-433.29" font-family="Times-Roman" font-size="14.00">Optics</text> <text text-anchor="middle" x="319" y="-297.25" font-family="Times-Roman" font-size="14.00">Optics</text>
</g> </g>
<!-- u4&#45;&gt;u3 --> <!-- u4&#45;&gt;u3 -->
<g id="edge48" class="edge"> <g id="edge18" class="edge">
<title>u4&#45;&gt;u3</title> <title>u4&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1318.68,-419.51C1306.65,-400.3 1286.64,-368.33 1272.68,-346.04"/> <path fill="none" stroke="black" d="M335.19,-284.17C346.87,-272.78 362.8,-257.24 375.83,-244.55"/>
<polygon fill="black" stroke="black" points="1275.57,-344.05 1267.3,-337.44 1269.64,-347.77 1275.57,-344.05"/> <polygon fill="black" stroke="black" points="378.39,-246.94 383.11,-237.45 373.5,-241.92 378.39,-246.94"/>
</g> </g>
<!-- u6 --> <!-- u6 -->
<g id="node29" class="node"> <g id="node12" class="node">
<title>u6</title> <title>u6</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="934" cy="-571.99" rx="64.19" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="638" cy="-486.95" rx="64.19" ry="18"/>
<text text-anchor="middle" x="934" y="-568.29" font-family="Times-Roman" font-size="14.00">MegaParsec</text> <text text-anchor="middle" x="638" y="-483.25" font-family="Times-Roman" font-size="14.00">MegaParsec</text>
</g> </g>
<!-- u5&#45;&gt;u6 --> <!-- u5&#45;&gt;u6 -->
<g id="edge49" class="edge"> <g id="edge19" class="edge">
<title>u5&#45;&gt;u6</title> <title>u5&#45;&gt;u6</title>
<path fill="none" stroke="black" d="M1225.64,-453.75C1216.72,-464.19 1203.86,-477.47 1190,-485.99 1117.08,-530.81 1088.16,-518.79 1007,-545.99 999.06,-548.65 990.64,-551.5 982.45,-554.3"/> <path fill="none" stroke="black" d="M425.28,-317.32C443.67,-335.87 472.3,-364.95 474,-367.95 491,-397.97 469.27,-418.9 494,-442.95 518.89,-467.15 536.76,-450.95 570,-460.95 577.45,-463.2 585.27,-465.82 592.85,-468.5"/>
<polygon fill="black" stroke="black" points="981.32,-550.99 972.99,-557.53 983.58,-557.61 981.32,-550.99"/> <polygon fill="black" stroke="black" points="591.68,-471.8 602.27,-471.91 594.06,-465.22 591.68,-471.8"/>
</g> </g>
<!-- u7 --> <!-- u7 -->
<g id="node30" class="node"> <g id="node13" class="node">
<title>u7</title> <title>u7</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1030" cy="-319.99" rx="44.39" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="618" cy="-220.95" rx="44.39" ry="18"/>
<text text-anchor="middle" x="1030" y="-316.29" font-family="Times-Roman" font-size="14.00">Prelude</text> <text text-anchor="middle" x="618" y="-217.25" font-family="Times-Roman" font-size="14.00">Prelude</text>
</g> </g>
<!-- u5&#45;&gt;u7 --> <!-- u5&#45;&gt;u7 -->
<g id="edge50" class="edge"> <g id="edge20" class="edge">
<title>u5&#45;&gt;u7</title> <title>u5&#45;&gt;u7</title>
<path fill="none" stroke="black" d="M1218.11,-422.39C1212.34,-418.59 1205.98,-414.53 1200,-410.99 1154.95,-384.33 1101.33,-356.57 1066.52,-339.06"/> <path fill="none" stroke="black" d="M429.11,-285.89C434.97,-281.99 441.58,-277.99 448,-274.95 487.67,-256.18 535.63,-241.92 570.48,-232.95"/>
<polygon fill="black" stroke="black" points="1067.74,-335.75 1057.23,-334.4 1064.6,-342.01 1067.74,-335.75"/> <polygon fill="black" stroke="black" points="571.56,-236.29 580.4,-230.45 569.85,-229.51 571.56,-236.29"/>
</g> </g>
<!-- u9 --> <!-- u9 -->
<g id="node25" class="node"> <g id="node8" class="node">
<title>u9</title> <title>u9</title>
<ellipse fill="#77ff77" stroke="black" stroke-width="0" cx="803" cy="-436.99" rx="51.19" ry="18"/> <ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="699" cy="-300.95" rx="51.19" ry="18"/>
<text text-anchor="middle" x="803" y="-433.29" font-family="Times-Roman" font-size="14.00">Common</text> <text text-anchor="middle" x="699" y="-297.25" font-family="Times-Roman" font-size="14.00">Common</text>
</g> </g>
<!-- u12&#45;&gt;u9 --> <!-- u12&#45;&gt;u9 -->
<g id="edge59" class="edge"> <g id="edge30" class="edge">
<title>u12&#45;&gt;u9</title> <title>u12&#45;&gt;u9</title>
<path fill="none" stroke="black" d="M822.17,-553.87C818.46,-531.43 811.9,-491.79 807.5,-465.2"/> <path fill="none" stroke="black" d="M690.07,-375.84C691.65,-362.75 693.87,-344.43 695.69,-329.32"/>
<polygon fill="black" stroke="black" points="810.91,-464.33 805.82,-455.04 804,-465.48 810.91,-464.33"/> <polygon fill="black" stroke="black" points="699.21,-329.34 696.94,-318.99 692.27,-328.5 699.21,-329.34"/>
</g> </g>
<!-- u9&#45;&gt;u7 --> <!-- u9&#45;&gt;u7 -->
<g id="edge60" class="edge"> <g id="edge31" class="edge">
<title>u9&#45;&gt;u7</title> <title>u9&#45;&gt;u7</title>
<path fill="none" stroke="black" d="M831.29,-421.66C872.57,-400.75 949.27,-361.89 994.36,-339.05"/> <path fill="none" stroke="black" d="M682.22,-283.79C670.52,-272.53 654.74,-257.34 641.76,-244.83"/>
<polygon fill="black" stroke="black" points="996.13,-342.07 1003.47,-334.43 992.97,-335.83 996.13,-342.07"/> <polygon fill="black" stroke="black" points="644.12,-242.24 634.48,-237.83 639.26,-247.29 644.12,-242.24"/>
</g> </g>
<!-- u10 --> <!-- u10 -->
<g id="node26" class="node"> <g id="node9" class="node">
<title>u10</title> <title>u10</title>
<ellipse fill="#ffff77" stroke="black" stroke-width="0" cx="1030" cy="-80.99" rx="27" ry="18"/> <ellipse fill="#77ff77" stroke="black" stroke-width="0" cx="618" cy="-55.95" rx="27" ry="18"/>
<text text-anchor="middle" x="1030" y="-77.29" font-family="Times-Roman" font-size="14.00">QQ</text> <text text-anchor="middle" x="618" y="-52.25" font-family="Times-Roman" font-size="14.00">QQ</text>
</g> </g>
<!-- u16 --> <!-- u16 -->
<g id="node27" class="node"> <g id="node10" class="node">
<title>u16</title> <title>u16</title>
<ellipse fill="#7777ff" stroke="black" stroke-width="0" cx="629" cy="-571.99" rx="27" ry="18"/> <ellipse fill="#ffff77" stroke="black" stroke-width="0" cx="747" cy="-486.95" rx="27" ry="18"/>
<text text-anchor="middle" x="629" y="-568.29" font-family="Times-Roman" font-size="14.00">QQ</text> <text text-anchor="middle" x="747" y="-483.25" font-family="Times-Roman" font-size="14.00">QQ</text>
</g> </g>
<!-- u15&#45;&gt;u1 --> <!-- u15 -->
<g id="edge51" class="edge"> <g id="node11" class="node">
<title>u15&#45;&gt;u1</title> <title>u15</title>
<path fill="none" stroke="black" d="M707.81,-589.96C711.74,-608.15 720.7,-636.49 740,-651.99 869.4,-755.91 944.8,-686.54 1106,-725.99 1109.21,-726.78 1112.51,-727.62 1115.82,-728.5"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="823" cy="-486.95" rx="30.59" ry="18"/>
<polygon fill="black" stroke="black" points="1115.2,-731.95 1125.77,-731.2 1117.04,-725.2 1115.2,-731.95"/> <text text-anchor="middle" x="823" y="-483.25" font-family="Times-Roman" font-size="14.00">Utils</text>
</g>
<!-- u15&#45;&gt;u12 -->
<g id="edge22" class="edge">
<title>u15&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M803.42,-472.71C797.8,-468.92 791.66,-464.77 786,-460.95 761.96,-444.74 734.59,-426.31 714.9,-413.05"/>
<polygon fill="black" stroke="black" points="716.68,-410.03 706.43,-407.35 712.77,-415.84 716.68,-410.03"/>
</g>
<!-- u15&#45;&gt;u11 -->
<g id="edge21" class="edge">
<title>u15&#45;&gt;u11</title>
<path fill="none" stroke="black" d="M805.88,-471.91C799.92,-467.72 792.97,-463.55 786,-460.95 723.08,-437.5 693.45,-477.7 636,-442.95 627.23,-437.65 620.23,-429.02 614.98,-420.54"/>
<polygon fill="black" stroke="black" points="617.89,-418.57 609.96,-411.54 611.78,-421.98 617.89,-418.57"/>
</g> </g>
<!-- u6&#45;&gt;u3 --> <!-- u6&#45;&gt;u3 -->
<g id="edge52" class="edge"> <g id="edge23" class="edge">
<title>u6&#45;&gt;u3</title> <title>u6&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M972.89,-557.62C1042.12,-533.81 1179.56,-486.5 1180,-485.99 1202.36,-460.38 1181.75,-442.3 1195,-410.99 1205.36,-386.51 1222.94,-361.88 1236.75,-344.58"/> <path fill="none" stroke="black" d="M608.52,-470.77C594.78,-463.16 578.53,-453.36 565,-442.95 527.38,-414.03 523.93,-400.08 489,-367.95 479.95,-359.63 474.21,-360.56 468,-349.95 450.82,-320.62 469.18,-304.85 453,-274.95 446.44,-262.83 436.18,-251.74 426.36,-242.87"/>
<polygon fill="black" stroke="black" points="1239.56,-346.68 1243.17,-336.72 1234.13,-342.25 1239.56,-346.68"/> <polygon fill="black" stroke="black" points="428.53,-240.12 418.66,-236.26 423.97,-245.43 428.53,-240.12"/>
</g> </g>
<!-- u8 --> <!-- u8 -->
<g id="node31" class="node"> <g id="node14" class="node">
<title>u8</title> <title>u8</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1030" cy="-202.99" rx="42.49" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="618" cy="-139.95" rx="42.49" ry="18"/>
<text text-anchor="middle" x="1030" y="-199.29" font-family="Times-Roman" font-size="14.00">Logger</text> <text text-anchor="middle" x="618" y="-136.25" font-family="Times-Roman" font-size="14.00">Logger</text>
</g> </g>
<!-- u7&#45;&gt;u8 --> <!-- u7&#45;&gt;u8 -->
<g id="edge54" class="edge"> <g id="edge25" class="edge">
<title>u7&#45;&gt;u8</title> <title>u7&#45;&gt;u8</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1030,-301.52C1030,-282.93 1030,-253.23 1030,-231.49"/> <path fill="none" stroke="black" stroke-dasharray="5,2" d="M618,-202.81C618,-192.67 618,-179.59 618,-168.07"/>
<polygon fill="black" stroke="black" points="1033.5,-231.24 1030,-221.24 1026.5,-231.24 1033.5,-231.24"/> <polygon fill="black" stroke="black" points="621.5,-168.06 618,-158.06 614.5,-168.06 621.5,-168.06"/>
</g> </g>
<!-- u2 --> <!-- u2 -->
<g id="node34" class="node"> <g id="node17" class="node">
<title>u2</title> <title>u2</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1128" cy="-436.99" rx="37.89" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="520" cy="-300.95" rx="37.89" ry="18"/>
<text text-anchor="middle" x="1128" y="-433.29" font-family="Times-Roman" font-size="14.00">Errors</text> <text text-anchor="middle" x="520" y="-297.25" font-family="Times-Roman" font-size="14.00">Errors</text>
</g> </g>
<!-- u7&#45;&gt;u2 --> <!-- u7&#45;&gt;u2 -->
<g id="edge53" class="edge"> <g id="edge24" class="edge">
<title>u7&#45;&gt;u2</title> <title>u7&#45;&gt;u2</title>
<path fill="none" stroke="black" d="M1043.83,-337.21C1060.5,-356.78 1088.66,-389.83 1107.78,-412.26"/> <path fill="none" stroke="black" d="M598.83,-237.21C584.07,-248.96 563.49,-265.34 547.06,-278.42"/>
<polygon fill="black" stroke="black" points="1105.22,-414.66 1114.37,-420 1110.55,-410.12 1105.22,-414.66"/> <polygon fill="black" stroke="black" points="544.54,-275.95 538.9,-284.91 548.9,-281.43 544.54,-275.95"/>
</g> </g>
<!-- u8&#45;&gt;u4 --> <!-- u8&#45;&gt;u4 -->
<g id="edge55" class="edge"> <g id="edge26" class="edge">
<title>u8&#45;&gt;u4</title> <title>u8&#45;&gt;u4</title>
<path fill="none" stroke="black" d="M1072.08,-205.27C1132.49,-209.51 1242.99,-226.65 1302,-293.99 1329.81,-325.73 1332.74,-377.05 1331.47,-408.52"/> <path fill="none" stroke="black" d="M576.88,-144.38C511.17,-150.66 386.55,-166.09 354,-194.95 331.93,-214.52 323.74,-248.37 320.73,-272.34"/>
<polygon fill="black" stroke="black" points="1327.96,-408.58 1330.88,-418.77 1334.95,-408.98 1327.96,-408.58"/> <polygon fill="black" stroke="black" points="317.22,-272.27 319.67,-282.57 324.18,-272.99 317.22,-272.27"/>
</g> </g>
<!-- u8&#45;&gt;u9 --> <!-- u8&#45;&gt;u9 -->
<g id="edge56" class="edge"> <g id="edge27" class="edge">
<title>u8&#45;&gt;u9</title> <title>u8&#45;&gt;u9</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1014.36,-219.98C975.12,-260.08 872.38,-365.08 826.14,-412.34"/> <path fill="none" stroke="black" stroke-dasharray="5,2" d="M637.21,-156.09C648.59,-166.01 662.41,-179.89 671,-194.95 684.9,-219.32 692.1,-250.8 695.69,-272.91"/>
<polygon fill="black" stroke="black" points="823.45,-410.08 818.96,-419.68 828.46,-414.98 823.45,-410.08"/> <polygon fill="black" stroke="black" points="692.25,-273.56 697.18,-282.94 699.17,-272.53 692.25,-273.56"/>
</g> </g>
<!-- u8&#45;&gt;u10 --> <!-- u8&#45;&gt;u10 -->
<g id="edge57" class="edge"> <g id="edge28" class="edge">
<title>u8&#45;&gt;u10</title> <title>u8&#45;&gt;u10</title>
<path fill="none" stroke="black" d="M1030,-184.8C1030,-165.1 1030,-132.57 1030,-109.38"/> <path fill="none" stroke="black" d="M618,-121.56C618,-110.73 618,-96.56 618,-84.25"/>
<polygon fill="black" stroke="black" points="1033.5,-109.15 1030,-99.15 1026.5,-109.15 1033.5,-109.15"/> <polygon fill="black" stroke="black" points="621.5,-84.04 618,-74.04 614.5,-84.04 621.5,-84.04"/>
</g> </g>
<!-- u11&#45;&gt;u5 --> <!-- u11&#45;&gt;u5 -->
<g id="edge58" class="edge"> <g id="edge29" class="edge">
<title>u11&#45;&gt;u5</title> <title>u11&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1062.1,-557.23C1067.11,-553.42 1072.68,-549.4 1078,-545.99 1125.54,-515.51 1144.66,-519.65 1190,-485.99 1199.71,-478.78 1209.34,-469.64 1217.41,-461.28"/> <path fill="none" stroke="black" d="M575.99,-387.58C537.44,-379.55 469,-363.89 448,-349.95 439.06,-344.02 431.26,-335.31 425.1,-326.92"/>
<polygon fill="black" stroke="black" points="1220.04,-463.58 1224.33,-453.89 1214.94,-458.8 1220.04,-463.58"/> <polygon fill="black" stroke="black" points="427.82,-324.71 419.28,-318.44 422.05,-328.67 427.82,-324.71"/>
</g>
<!-- u0 -->
<g id="node16" class="node">
<title>u0</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="2" cx="627" cy="-605.95" rx="42.49" ry="18"/>
<text text-anchor="middle" x="627" y="-602.25" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g>
<!-- u0&#45;&gt;u1 -->
<g id="edge4" class="edge">
<title>u0&#45;&gt;u1</title>
<path fill="none" stroke="black" d="M584.67,-604.4C545.83,-602.07 488.09,-593.82 446,-566.95 425.91,-554.13 410.22,-531.89 400.09,-514.27"/>
<polygon fill="black" stroke="black" points="402.91,-512.13 395.04,-505.04 396.77,-515.5 402.91,-512.13"/>
</g> </g>
<!-- u0&#45;&gt;u16 --> <!-- u0&#45;&gt;u16 -->
<g id="edge4" class="edge"> <g id="edge6" class="edge">
<title>u0&#45;&gt;u16</title> <title>u0&#45;&gt;u16</title>
<path fill="none" stroke="black" d="M700.59,-847.09C687.91,-798.57 651.35,-658.57 635.96,-599.64"/> <path fill="none" stroke="black" d="M663.33,-596.57C681.18,-590.83 701.84,-581.54 716,-566.95 729.77,-552.77 737.62,-531.76 741.97,-514.97"/>
<polygon fill="black" stroke="black" points="639.34,-598.73 633.43,-589.94 632.57,-600.5 639.34,-598.73"/> <polygon fill="black" stroke="black" points="745.4,-515.69 744.26,-505.16 738.58,-514.11 745.4,-515.69"/>
</g> </g>
<!-- u0&#45;&gt;u15 --> <!-- u0&#45;&gt;u15 -->
<g id="edge3" class="edge"> <g id="edge5" class="edge">
<title>u0&#45;&gt;u15</title> <title>u0&#45;&gt;u15</title>
<path fill="none" stroke="black" d="M705,-846.66C705,-797.93 705,-659.45 705,-600.31"/> <path fill="none" stroke="black" d="M669.23,-604.64C704.08,-602.27 753,-593.85 786,-566.95 801.95,-553.96 811.39,-532.52 816.74,-515.25"/>
<polygon fill="black" stroke="black" points="708.5,-600.23 705,-590.23 701.5,-600.23 708.5,-600.23"/> <polygon fill="black" stroke="black" points="820.24,-515.72 819.56,-505.15 813.5,-513.84 820.24,-515.72"/>
</g>
<!-- u14 -->
<g id="node19" class="node">
<title>u14</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="508" cy="-486.95" rx="48.19" ry="18"/>
<text text-anchor="middle" x="508" y="-483.25" font-family="Times-Roman" font-size="14.00">Platform</text>
</g> </g>
<!-- u0&#45;&gt;u14 --> <!-- u0&#45;&gt;u14 -->
<g id="edge2" class="edge"> <g id="edge3" class="edge">
<title>u0&#45;&gt;u14</title> <title>u0&#45;&gt;u14</title>
<path fill="none" stroke="black" d="M730.82,-850.57C771.64,-829.36 851.2,-788.02 897.73,-763.84"/> <path fill="none" stroke="black" d="M599.25,-592.23C586.98,-585.76 572.9,-577.11 562,-566.95 545.64,-551.71 531.56,-530.66 521.88,-514.12"/>
<polygon fill="black" stroke="black" points="899.59,-766.81 906.85,-759.1 896.37,-760.6 899.59,-766.81"/> <polygon fill="black" stroke="black" points="524.68,-511.96 516.71,-504.98 518.59,-515.41 524.68,-511.96"/>
</g> </g>
<!-- u2&#45;&gt;u3 --> <!-- u2&#45;&gt;u3 -->
<g id="edge5" class="edge"> <g id="edge7" class="edge">
<title>u2&#45;&gt;u3</title> <title>u2&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1145.13,-420.72C1167.39,-400.87 1206.44,-366.06 1232.01,-343.27"/> <path fill="none" stroke="black" d="M498.37,-286.01C479.01,-273.53 450.45,-255.12 428.91,-241.23"/>
<polygon fill="black" stroke="black" points="1234.66,-345.59 1239.8,-336.32 1230.01,-340.37 1234.66,-345.59"/> <polygon fill="black" stroke="black" points="430.75,-238.25 420.45,-235.78 426.95,-244.14 430.75,-238.25"/>
</g> </g>
<!-- u13&#45;&gt;u3 --> <!-- u13&#45;&gt;u3 -->
<g id="edge6" class="edge"> <g id="edge8" class="edge">
<title>u13&#45;&gt;u3</title> <title>u13&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1413.93,-553.61C1413.03,-522.51 1407.48,-456.35 1378,-410.99 1356.72,-378.25 1319.17,-353.11 1291.53,-337.83"/> <path fill="none" stroke="black" d="M144.85,-290.31C198.89,-275.78 300.93,-248.33 357.9,-233.01"/>
<polygon fill="black" stroke="black" points="1293.04,-334.67 1282.57,-333.03 1289.73,-340.84 1293.04,-334.67"/> <polygon fill="black" stroke="black" points="358.91,-236.36 367.66,-230.38 357.09,-229.6 358.91,-236.36"/>
</g> </g>
<!-- u14&#45;&gt;u5 --> <!-- u14&#45;&gt;u5 -->
<g id="edge7" class="edge"> <g id="edge9" class="edge">
<title>u14&#45;&gt;u5</title> <title>u14&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M970.19,-731.9C1005.99,-718.88 1059.19,-693.62 1086,-651.99 1111.67,-612.13 1073.1,-586.34 1098,-545.99 1123.63,-504.44 1152.09,-516.75 1190,-485.99 1199.39,-478.37 1208.96,-469.15 1217.06,-460.83"/> <path fill="none" stroke="black" d="M492.36,-469.49C485.88,-461.9 478.78,-452.48 474,-442.95 458.54,-412.12 468.2,-399.39 454,-367.95 447.34,-353.21 437.53,-338.1 428.85,-326.06"/>
<polygon fill="black" stroke="black" points="1219.67,-463.17 1224.04,-453.52 1214.6,-458.34 1219.67,-463.17"/> <polygon fill="black" stroke="black" points="431.57,-323.84 422.81,-317.89 425.94,-328.01 431.57,-323.84"/>
</g> </g>
<!-- u14&#45;&gt;u12 --> <!-- u14&#45;&gt;u12 -->
<g id="edge8" class="edge"> <g id="edge10" class="edge">
<title>u14&#45;&gt;u12</title> <title>u14&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M917.2,-726.88C899.99,-709.65 873.37,-680.88 856,-651.99 846.1,-635.52 838.24,-615.33 832.91,-599.48"/> <path fill="none" stroke="black" d="M534.95,-471.89C543.43,-467.9 552.95,-463.86 562,-460.95 602.92,-447.83 621.35,-466.95 657,-442.95 665.13,-437.48 671.61,-429.08 676.49,-420.83"/>
<polygon fill="black" stroke="black" points="836.18,-598.22 829.78,-589.77 829.52,-600.36 836.18,-598.22"/> <polygon fill="black" stroke="black" points="679.72,-422.21 681.33,-411.74 673.54,-418.92 679.72,-422.21"/>
</g>
<!-- u18 -->
<g id="node20" class="node">
<title>u18</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="109" cy="-393.95" rx="73.39" ry="18"/>
<text text-anchor="middle" x="109" y="-390.25" font-family="Times-Roman" font-size="14.00">Requirements</text>
</g> </g>
<!-- u18&#45;&gt;u5 --> <!-- u18&#45;&gt;u5 -->
<g id="edge10" class="edge"> <g id="edge12" class="edge">
<title>u18&#45;&gt;u5</title> <title>u18&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1467.83,-725.94C1481.37,-689.08 1507.02,-600.87 1467,-545.99 1415.57,-475.47 1352.95,-533.91 1280,-485.99 1270.56,-479.79 1261.98,-470.91 1255.06,-462.47"/> <path fill="none" stroke="black" d="M147.73,-378.58C159.84,-374.58 173.34,-370.6 186,-367.95 265.56,-351.31 295.73,-387.15 368,-349.95 378.45,-344.58 387.45,-335.41 394.41,-326.53"/>
<polygon fill="black" stroke="black" points="1257.62,-460.06 1248.74,-454.29 1252.09,-464.34 1257.62,-460.06"/> <polygon fill="black" stroke="black" points="397.39,-328.37 400.44,-318.22 391.73,-324.26 397.39,-328.37"/>
</g> </g>
<!-- u18&#45;&gt;u13 --> <!-- u18&#45;&gt;u13 -->
<g id="edge9" class="edge"> <g id="edge11" class="edge">
<title>u18&#45;&gt;u13</title> <title>u18&#45;&gt;u13</title>
<path fill="none" stroke="black" d="M1456.23,-725.75C1448.05,-696.15 1431.26,-635.42 1421.5,-600.1"/> <path fill="none" stroke="black" d="M109,-375.84C109,-362.75 109,-344.43 109,-329.32"/>
<polygon fill="black" stroke="black" points="1424.75,-598.72 1418.71,-590.02 1418,-600.59 1424.75,-598.72"/> <polygon fill="black" stroke="black" points="112.5,-328.99 109,-318.99 105.5,-328.99 112.5,-328.99"/>
</g> </g>
<!-- u20 --> <!-- u20 -->
<g id="node38" class="node"> <g id="node21" class="node">
<title>u20</title> <title>u20</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="742" cy="-1387.99" rx="32.49" ry="18"/> <ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="627" cy="-769.95" rx="32.49" ry="18"/>
<text text-anchor="middle" x="742" y="-1384.29" font-family="Times-Roman" font-size="14.00">Main</text> <text text-anchor="middle" x="627" y="-766.25" font-family="Times-Roman" font-size="14.00">Main</text>
</g>
<!-- u21 -->
<g id="node22" class="node">
<title>u21</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="627" cy="-689.95" rx="46.29" ry="18"/>
<text text-anchor="middle" x="627" y="-686.25" font-family="Times-Roman" font-size="14.00">Validate</text>
</g> </g>
<!-- u20&#45;&gt;u21 --> <!-- u20&#45;&gt;u21 -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>u20&#45;&gt;u21</title> <title>u20&#45;&gt;u21</title>
<path fill="none" stroke="black" d="M742,-1369.75C742,-1340.15 742,-1279.42 742,-1244.1"/> <path fill="none" stroke="black" d="M627,-751.64C627,-741.85 627,-729.38 627,-718.29"/>
<polygon fill="black" stroke="black" points="745.5,-1244.02 742,-1234.02 738.5,-1244.02 745.5,-1244.02"/> <polygon fill="black" stroke="black" points="630.5,-718.22 627,-708.22 623.5,-718.22 630.5,-718.22"/>
</g>
<!-- u21&#45;&gt;u0 -->
<g id="edge2" class="edge">
<title>u21&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M627,-671.56C627,-660.73 627,-646.56 627,-634.25"/>
<polygon fill="black" stroke="black" points="630.5,-634.04 627,-624.04 623.5,-634.04 630.5,-634.04"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -4,638 +4,363 @@
<!-- Generated by graphviz version 2.44.0 (0) <!-- Generated by graphviz version 2.44.0 (0)
--> -->
<!-- Title: G Pages: 1 --> <!-- Title: G Pages: 1 -->
<svg width="1076pt" height="648pt" <svg width="1075pt" height="648pt"
viewBox="0.00 0.00 1076.37 648.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> viewBox="0.00 0.00 1075.16 648.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(0.68 0.68) rotate(0) translate(4 949)"> <g id="graph0" class="graph" transform="scale(0.91 0.91) rotate(0) translate(4 710)">
<title>G</title> <title>G</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-949 1579,-949 1579,4 -4,4"/> <polygon fill="white" stroke="transparent" points="-4,4 -4,-710 1180.67,-710 1180.67,4 -4,4"/>
<g id="clust1" class="cluster"> <g id="clust1" class="cluster">
<title>cluster_0</title> <title>cluster_0</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="8,-8.66 8,-897.37 1567,-897.37 1567,-8.66 8,-8.66"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="10.72,-8 10.72,-590 1165.95,-590 1165.95,-8 10.72,-8"/>
<text text-anchor="middle" x="787.5" y="-882.17" font-family="Times-Roman" font-size="14.00">GHCup</text> <text text-anchor="middle" x="588.33" y="-574.8" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g> </g>
<g id="clust2" class="cluster"> <g id="clust2" class="cluster">
<title>cluster_1</title> <title>cluster_1</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="1102,-352.89 1102,-548.81 1362,-548.81 1362,-352.89 1102,-352.89"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="254.63,-326 254.63,-484 603.08,-484 603.08,-326 254.63,-326"/>
<text text-anchor="middle" x="1232" y="-533.61" font-family="Times-Roman" font-size="14.00">Download</text> <text text-anchor="middle" x="428.85" y="-468.8" font-family="Times-Roman" font-size="14.00">Download</text>
</g> </g>
<g id="clust3" class="cluster"> <g id="clust3" class="cluster">
<title>cluster_2</title> <title>cluster_2</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="16,-626.75 16,-863.81 1559,-863.81 1559,-626.75 16,-626.75"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="363.19,-171 363.19,-318 605.76,-318 605.76,-171 363.19,-171"/>
<text text-anchor="middle" x="787.5" y="-848.61" font-family="Times-Roman" font-size="14.00">OptParse</text> <text text-anchor="middle" x="484.47" y="-302.8" font-family="Times-Roman" font-size="14.00">Types</text>
</g> </g>
<g id="clust4" class="cluster"> <g id="clust4" class="cluster">
<title>cluster_3</title> <title>cluster_3</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="1196,-185.1 1196,-344.23 1377,-344.23 1377,-185.1 1196,-185.1"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="758.53,-16 758.53,-515 1155.22,-515 1155.22,-16 758.53,-16"/>
<text text-anchor="middle" x="1286.5" y="-329.03" font-family="Times-Roman" font-size="14.00">Types</text> <text text-anchor="middle" x="956.88" y="-499.8" font-family="Times-Roman" font-size="14.00">Utils</text>
</g> </g>
<g id="clust5" class="cluster"> <g id="clust5" class="cluster">
<title>cluster_4</title> <title>cluster_4</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="586,-17.32 586,-467.63 1082,-467.63 1082,-17.32 586,-17.32"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="857.71,-243 857.71,-401 1015.85,-401 1015.85,-243 857.71,-243"/>
<text text-anchor="middle" x="834" y="-452.43" font-family="Times-Roman" font-size="14.00">Utils</text> <text text-anchor="middle" x="936.78" y="-385.8" font-family="Times-Roman" font-size="14.00">File</text>
</g> </g>
<g id="clust6" class="cluster"> <g id="clust6" class="cluster">
<title>cluster_5</title> <title>cluster_5</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="744,-263.04 744,-434.07 862,-434.07 862,-263.04 744,-263.04"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="781.32,-24 781.32,-99 875.13,-99 875.13,-24 781.32,-24"/>
<text text-anchor="middle" x="803" y="-418.87" font-family="Times-Roman" font-size="14.00">File</text> <text text-anchor="middle" x="828.22" y="-83.8" font-family="Times-Roman" font-size="14.00">String</text>
</g> </g>
<g id="clust7" class="cluster"> <g id="clust7" class="cluster">
<title>cluster_6</title> <title>cluster_6</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="995,-25.98 995,-107.16 1065,-107.16 1065,-25.98 995,-25.98"/> <polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="954.2,-409 954.2,-484 1048.01,-484 1048.01,-409 954.2,-409"/>
<text text-anchor="middle" x="1030" y="-91.96" font-family="Times-Roman" font-size="14.00">String</text> <text text-anchor="middle" x="1001.1" y="-468.8" font-family="Times-Roman" font-size="14.00">Version</text>
</g>
<g id="clust8" class="cluster">
<title>cluster_7</title>
<polygon fill="#000000" fill-opacity="0.058824" stroke="#000000" stroke-opacity="0.058824" points="594,-352.89 594,-434.07 664,-434.07 664,-352.89 594,-352.89"/>
<text text-anchor="middle" x="629" y="-418.87" font-family="Times-Roman" font-size="14.00">Version</text>
</g> </g>
<!-- u1 --> <!-- u1 -->
<g id="node1" class="node"> <g id="node1" class="node">
<title>u1</title> <title>u1</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="1165" cy="-495.66" rx="55.49" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="518.39" cy="-435" rx="55.49" ry="18"/>
<text text-anchor="middle" x="1165" y="-491.96" font-family="Times-Roman" font-size="14.00">Download</text> <text text-anchor="middle" x="518.39" y="-431.3" font-family="Times-Roman" font-size="14.00">Download</text>
</g> </g>
<!-- u12 --> <!-- u12 -->
<g id="node24" class="node"> <g id="node7" class="node">
<title>u12</title> <title>u12</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="2" cx="825" cy="-380.66" rx="27" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="2" cx="922.39" cy="-352" rx="27" ry="18"/>
<text text-anchor="middle" x="825" y="-376.96" font-family="Times-Roman" font-size="14.00">File</text> <text text-anchor="middle" x="922.39" y="-348.3" font-family="Times-Roman" font-size="14.00">File</text>
</g> </g>
<!-- u1&#45;&gt;u12 --> <!-- u1&#45;&gt;u12 -->
<g id="edge13" class="edge"> <g id="edge15" class="edge">
<title>u1&#45;&gt;u12</title> <title>u1&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M1127.19,-482.48C1120.21,-480.6 1112.93,-478.88 1106,-477.66 1078.58,-472.83 878.46,-477.12 856,-460.66 839.51,-448.58 831.79,-426.38 828.17,-408.59"/> <path fill="none" stroke="black" d="M546.79,-419.43C556.27,-415.25 567.06,-411.24 577.39,-409 594.44,-405.31 876.17,-409.54 891.39,-401 900.16,-396.08 906.84,-387.53 911.72,-379"/>
<polygon fill="black" stroke="black" points="831.62,-407.99 826.49,-398.73 824.72,-409.17 831.62,-407.99"/> <polygon fill="black" stroke="black" points="914.92,-380.43 916.31,-369.92 908.67,-377.26 914.92,-380.43"/>
</g> </g>
<!-- u11 --> <!-- u11 -->
<g id="node32" class="node"> <g id="node15" class="node">
<title>u11</title> <title>u11</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1045" cy="-380.66" rx="28.7" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="808.39" cy="-352" rx="28.7" ry="18"/>
<text text-anchor="middle" x="1045" y="-376.96" font-family="Times-Roman" font-size="14.00">Dirs</text> <text text-anchor="middle" x="808.39" y="-348.3" font-family="Times-Roman" font-size="14.00">Dirs</text>
</g> </g>
<!-- u1&#45;&gt;u11 --> <!-- u1&#45;&gt;u11 -->
<g id="edge12" class="edge"> <g id="edge14" class="edge">
<title>u1&#45;&gt;u11</title> <title>u1&#45;&gt;u11</title>
<path fill="none" stroke="black" d="M1130.87,-481.48C1119.63,-476.1 1107.57,-469.13 1098,-460.66 1081.15,-445.74 1067.22,-424.32 1057.88,-407.55"/> <path fill="none" stroke="black" d="M548.95,-419.93C557.99,-416.08 567.99,-412.11 577.39,-409 586.69,-405.92 709.34,-376.59 772.03,-361.65"/>
<polygon fill="black" stroke="black" points="1060.73,-405.46 1052.92,-398.3 1054.56,-408.76 1060.73,-405.46"/> <polygon fill="black" stroke="black" points="772.99,-365.02 781.91,-359.3 771.37,-358.21 772.99,-365.02"/>
</g> </g>
<!-- u13 --> <!-- u13 -->
<g id="node35" class="node"> <g id="node18" class="node">
<title>u13</title> <title>u13</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1414" cy="-380.66" rx="44.39" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="146.39" cy="-269" rx="44.39" ry="18"/>
<text text-anchor="middle" x="1414" y="-376.96" font-family="Times-Roman" font-size="14.00">Version</text> <text text-anchor="middle" x="146.39" y="-265.3" font-family="Times-Roman" font-size="14.00">Version</text>
</g> </g>
<!-- u1&#45;&gt;u13 --> <!-- u1&#45;&gt;u13 -->
<g id="edge11" class="edge"> <g id="edge13" class="edge">
<title>u1&#45;&gt;u13</title> <title>u1&#45;&gt;u13</title>
<path fill="none" stroke="black" d="M1205.09,-483.12C1212.97,-481.1 1221.21,-479.17 1229,-477.66 1259.12,-471.82 1340.24,-477.32 1366,-460.66 1384.87,-448.45 1397.46,-425.95 1405,-408.08"/> <path fill="none" stroke="black" d="M477.97,-422.58C457.84,-417.42 433.02,-411.85 410.39,-409 400.85,-407.8 71.11,-407.88 64.39,-401 41.09,-377.16 48.83,-355.48 64.39,-326 73.49,-308.76 90.55,-295.83 106.76,-286.76"/>
<polygon fill="black" stroke="black" points="1408.27,-409.31 1408.67,-398.72 1401.76,-406.75 1408.27,-409.31"/> <polygon fill="black" stroke="black" points="108.39,-289.86 115.62,-282.11 105.14,-283.66 108.39,-289.86"/>
</g> </g>
<!-- u17 --> <!-- u17 -->
<g id="node2" class="node"> <g id="node2" class="node">
<title>u17</title> <title>u17</title>
<ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="1267" cy="-380.66" rx="30.59" ry="18"/> <ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="445.39" cy="-352" rx="30.59" ry="18"/>
<text text-anchor="middle" x="1267" y="-376.96" font-family="Times-Roman" font-size="14.00">Utils</text> <text text-anchor="middle" x="445.39" y="-348.3" font-family="Times-Roman" font-size="14.00">Utils</text>
</g> </g>
<!-- u5 --> <!-- u5 -->
<g id="node23" class="node"> <g id="node6" class="node">
<title>u5</title> <title>u5</title>
<ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="1238" cy="-291.66" rx="33.6" ry="18"/> <ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="549.39" cy="-269" rx="33.6" ry="18"/>
<text text-anchor="middle" x="1238" y="-287.96" font-family="Times-Roman" font-size="14.00">JSON</text> <text text-anchor="middle" x="549.39" y="-265.3" font-family="Times-Roman" font-size="14.00">JSON</text>
</g> </g>
<!-- u17&#45;&gt;u5 --> <!-- u17&#45;&gt;u5 -->
<g id="edge14" class="edge"> <g id="edge16" class="edge">
<title>u17&#45;&gt;u5</title> <title>u17&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1261.41,-362.89C1257.3,-350.57 1251.64,-333.58 1246.91,-319.4"/> <path fill="none" stroke="black" d="M463.03,-337.26C479.49,-324.44 504.26,-305.15 523.05,-290.51"/>
<polygon fill="black" stroke="black" points="1250.16,-318.06 1243.67,-309.68 1243.52,-320.27 1250.16,-318.06"/> <polygon fill="black" stroke="black" points="525.22,-293.26 530.96,-284.35 520.92,-287.74 525.22,-293.26"/>
</g> </g>
<!-- u19 --> <!-- u19 -->
<g id="node3" class="node"> <g id="node3" class="node">
<title>u19</title> <title>u19</title>
<ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="1296" cy="-495.66" rx="57.69" ry="18"/> <ellipse fill="#bbbbff" stroke="black" stroke-width="0" cx="343.39" cy="-435" rx="57.69" ry="18"/>
<text text-anchor="middle" x="1296" y="-491.96" font-family="Times-Roman" font-size="14.00">IOStreams</text> <text text-anchor="middle" x="343.39" y="-431.3" font-family="Times-Roman" font-size="14.00">IOStreams</text>
</g> </g>
<!-- u19&#45;&gt;u17 --> <!-- u19&#45;&gt;u17 -->
<g id="edge15" class="edge">
<title>u19&#45;&gt;u17</title>
<path fill="none" stroke="black" d="M1291.6,-477.5C1286.88,-459.14 1279.35,-429.78 1273.86,-408.39"/>
<polygon fill="black" stroke="black" points="1277.23,-407.43 1271.35,-398.61 1270.45,-409.17 1277.23,-407.43"/>
</g>
<!-- u21 -->
<g id="node4" class="node">
<title>u21</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="742" cy="-810.66" rx="51.99" ry="18"/>
<text text-anchor="middle" x="742" y="-806.96" font-family="Times-Roman" font-size="14.00">OptParse</text>
</g>
<!-- u23 -->
<g id="node6" class="node">
<title>u23</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="846" cy="-732.66" rx="38.19" ry="18"/>
<text text-anchor="middle" x="846" y="-728.96" font-family="Times-Roman" font-size="14.00">Install</text>
</g>
<!-- u21&#45;&gt;u23 -->
<g id="edge16" class="edge">
<title>u21&#45;&gt;u23</title>
<path fill="none" stroke="black" d="M763.3,-794.1C778.98,-782.63 800.48,-766.92 817.63,-754.39"/>
<polygon fill="black" stroke="black" points="820.13,-756.9 826.14,-748.17 816,-751.25 820.13,-756.9"/>
</g>
<!-- u24 -->
<g id="node7" class="node">
<title>u24</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="929" cy="-732.66" rx="27" ry="18"/>
<text text-anchor="middle" x="929" y="-728.96" font-family="Times-Roman" font-size="14.00">Set</text>
</g>
<!-- u21&#45;&gt;u24 -->
<g id="edge17" class="edge"> <g id="edge17" class="edge">
<title>u21&#45;&gt;u24</title> <title>u19&#45;&gt;u17</title>
<path fill="none" stroke="black" d="M776.15,-797.08C806.94,-785.56 853.32,-767.76 893,-750.66 894.81,-749.88 896.66,-749.06 898.53,-748.23"/> <path fill="none" stroke="black" d="M378.72,-420.5C389.55,-415.38 401.06,-408.84 410.39,-401 418.16,-394.47 425.12,-385.87 430.76,-377.75"/>
<polygon fill="black" stroke="black" points="900.16,-751.33 907.79,-743.97 897.24,-744.97 900.16,-751.33"/> <polygon fill="black" stroke="black" points="433.77,-379.54 436.33,-369.25 427.92,-375.7 433.77,-379.54"/>
</g>
<!-- u25 -->
<g id="node8" class="node">
<title>u25</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1197" cy="-732.66" rx="38.19" ry="18"/>
<text text-anchor="middle" x="1197" y="-728.96" font-family="Times-Roman" font-size="14.00">UnSet</text>
</g>
<!-- u21&#45;&gt;u25 -->
<g id="edge18" class="edge">
<title>u21&#45;&gt;u25</title>
<path fill="none" stroke="black" d="M792.1,-805.59C869.52,-798.59 1022.5,-781.91 1149,-750.66 1152.18,-749.88 1155.44,-748.96 1158.69,-747.97"/>
<polygon fill="black" stroke="black" points="1159.91,-751.26 1168.34,-744.84 1157.75,-744.6 1159.91,-751.26"/>
</g>
<!-- u26 -->
<g id="node9" class="node">
<title>u26</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1001" cy="-732.66" rx="27" ry="18"/>
<text text-anchor="middle" x="1001" y="-728.96" font-family="Times-Roman" font-size="14.00">Rm</text>
</g>
<!-- u21&#45;&gt;u26 -->
<g id="edge19" class="edge">
<title>u21&#45;&gt;u26</title>
<path fill="none" stroke="black" d="M785.47,-800.63C830.94,-790.62 904.05,-772.9 965,-750.66 966.85,-749.98 968.74,-749.25 970.62,-748.47"/>
<polygon fill="black" stroke="black" points="972.2,-751.6 979.95,-744.38 969.39,-745.19 972.2,-751.6"/>
</g>
<!-- u27 -->
<g id="node10" class="node">
<title>u27</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1093" cy="-732.66" rx="47.39" ry="18"/>
<text text-anchor="middle" x="1093" y="-728.96" font-family="Times-Roman" font-size="14.00">Compile</text>
</g>
<!-- u21&#45;&gt;u27 -->
<g id="edge20" class="edge">
<title>u21&#45;&gt;u27</title>
<path fill="none" stroke="black" d="M787.79,-801.93C846.05,-791.76 949.62,-772.61 1037,-750.66 1040.61,-749.75 1044.34,-748.76 1048.07,-747.72"/>
<polygon fill="black" stroke="black" points="1049.25,-751.02 1057.9,-744.89 1047.32,-744.29 1049.25,-751.02"/>
</g>
<!-- u28 -->
<g id="node11" class="node">
<title>u28</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="64" cy="-732.66" rx="40.09" ry="18"/>
<text text-anchor="middle" x="64" y="-728.96" font-family="Times-Roman" font-size="14.00">Config</text>
</g>
<!-- u21&#45;&gt;u28 -->
<g id="edge21" class="edge">
<title>u21&#45;&gt;u28</title>
<path fill="none" stroke="black" d="M689.99,-808.99C581.5,-806.67 323.62,-796.37 113,-750.66 109.64,-749.93 106.2,-749.04 102.77,-748.06"/>
<polygon fill="black" stroke="black" points="103.8,-744.71 93.21,-745.08 101.72,-751.4 103.8,-744.71"/>
</g>
<!-- u29 -->
<g id="node12" class="node">
<title>u29</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="415" cy="-732.66" rx="46.59" ry="18"/>
<text text-anchor="middle" x="415" y="-728.96" font-family="Times-Roman" font-size="14.00">Whereis</text>
</g>
<!-- u21&#45;&gt;u29 -->
<g id="edge22" class="edge">
<title>u21&#45;&gt;u29</title>
<path fill="none" stroke="black" d="M697.62,-801.14C643.73,-790.53 550.22,-771.27 471,-750.66 467.24,-749.68 463.35,-748.61 459.45,-747.51"/>
<polygon fill="black" stroke="black" points="460.41,-744.14 449.83,-744.71 458.45,-750.86 460.41,-744.14"/>
</g>
<!-- u30 -->
<g id="node13" class="node">
<title>u30</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="507" cy="-732.66" rx="27" ry="18"/>
<text text-anchor="middle" x="507" y="-728.96" font-family="Times-Roman" font-size="14.00">List</text>
</g>
<!-- u21&#45;&gt;u30 -->
<g id="edge23" class="edge">
<title>u21&#45;&gt;u30</title>
<path fill="none" stroke="black" d="M700.99,-799.47C660.33,-788.91 596.53,-771.1 543,-750.66 541.16,-749.96 539.28,-749.2 537.4,-748.41"/>
<polygon fill="black" stroke="black" points="538.65,-745.13 528.09,-744.28 535.81,-751.53 538.65,-745.13"/>
</g>
<!-- u31 -->
<g id="node14" class="node">
<title>u31</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1303" cy="-732.66" rx="50.09" ry="18"/>
<text text-anchor="middle" x="1303" y="-728.96" font-family="Times-Roman" font-size="14.00">Upgrade</text>
</g>
<!-- u21&#45;&gt;u31 -->
<g id="edge24" class="edge">
<title>u21&#45;&gt;u31</title>
<path fill="none" stroke="black" d="M792.66,-806.46C883.72,-800.09 1080.77,-783.54 1244,-750.66 1248.17,-749.82 1252.48,-748.83 1256.78,-747.75"/>
<polygon fill="black" stroke="black" points="1257.95,-751.06 1266.72,-745.12 1256.16,-744.29 1257.95,-751.06"/>
</g>
<!-- u32 -->
<g id="node15" class="node">
<title>u32</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="614" cy="-732.66" rx="61.99" ry="18"/>
<text text-anchor="middle" x="614" y="-728.96" font-family="Times-Roman" font-size="14.00">ChangeLog</text>
</g>
<!-- u21&#45;&gt;u32 -->
<g id="edge25" class="edge">
<title>u21&#45;&gt;u32</title>
<path fill="none" stroke="black" d="M717.02,-794.83C697.66,-783.34 670.61,-767.27 649.09,-754.49"/>
<polygon fill="black" stroke="black" points="650.63,-751.34 640.24,-749.24 647.05,-757.36 650.63,-751.34"/>
</g>
<!-- u33 -->
<g id="node16" class="node">
<title>u33</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="742" cy="-732.66" rx="48.19" ry="18"/>
<text text-anchor="middle" x="742" y="-728.96" font-family="Times-Roman" font-size="14.00">Prefetch</text>
</g>
<!-- u21&#45;&gt;u33 -->
<g id="edge26" class="edge">
<title>u21&#45;&gt;u33</title>
<path fill="none" stroke="black" d="M742,-792.41C742,-783.18 742,-771.59 742,-761.15"/>
<polygon fill="black" stroke="black" points="745.5,-760.84 742,-750.84 738.5,-760.84 745.5,-760.84"/>
</g>
<!-- u34 -->
<g id="node17" class="node">
<title>u34</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="235" cy="-732.66" rx="27" ry="18"/>
<text text-anchor="middle" x="235" y="-728.96" font-family="Times-Roman" font-size="14.00">GC</text>
</g>
<!-- u21&#45;&gt;u34 -->
<g id="edge27" class="edge">
<title>u21&#45;&gt;u34</title>
<path fill="none" stroke="black" d="M690.05,-808.49C602.09,-805.33 418.98,-793.68 271,-750.66 269.06,-750.1 267.09,-749.44 265.13,-748.72"/>
<polygon fill="black" stroke="black" points="266.08,-745.32 255.5,-744.7 263.39,-751.78 266.08,-745.32"/>
</g>
<!-- u35 -->
<g id="node18" class="node">
<title>u35</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="315" cy="-732.66" rx="35.19" ry="18"/>
<text text-anchor="middle" x="315" y="-728.96" font-family="Times-Roman" font-size="14.00">DInfo</text>
</g>
<!-- u21&#45;&gt;u35 -->
<g id="edge28" class="edge">
<title>u21&#45;&gt;u35</title>
<path fill="none" stroke="black" d="M692.02,-805.43C618.22,-798.41 476.17,-781.93 359,-750.66 356.35,-749.95 353.63,-749.14 350.92,-748.26"/>
<polygon fill="black" stroke="black" points="351.81,-744.86 341.22,-744.85 349.49,-751.47 351.81,-744.86"/>
</g>
<!-- u36 -->
<g id="node19" class="node">
<title>u36</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="1461" cy="-732.66" rx="90.18" ry="18"/>
<text text-anchor="middle" x="1461" y="-728.96" font-family="Times-Roman" font-size="14.00">ToolRequirements</text>
</g>
<!-- u21&#45;&gt;u36 -->
<g id="edge29" class="edge">
<title>u21&#45;&gt;u36</title>
<path fill="none" stroke="black" d="M792.37,-806.16C898.17,-798.52 1151.13,-778.76 1362,-750.66 1370.05,-749.59 1378.45,-748.34 1386.8,-747.02"/>
<polygon fill="black" stroke="black" points="1387.62,-750.44 1396.94,-745.38 1386.51,-743.53 1387.62,-750.44"/>
</g>
<!-- u37 -->
<g id="node20" class="node">
<title>u37</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="156" cy="-732.66" rx="33.6" ry="18"/>
<text text-anchor="middle" x="156" y="-728.96" font-family="Times-Roman" font-size="14.00">Nuke</text>
</g>
<!-- u21&#45;&gt;u37 -->
<g id="edge30" class="edge">
<title>u21&#45;&gt;u37</title>
<path fill="none" stroke="black" d="M690.3,-808.63C592.81,-805.69 375.74,-794.35 199,-750.66 196.22,-749.97 193.39,-749.15 190.56,-748.24"/>
<polygon fill="black" stroke="black" points="191.69,-744.93 181.1,-744.88 189.35,-751.52 191.69,-744.93"/>
</g>
<!-- u22 -->
<g id="node5" class="node">
<title>u22</title>
<ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="742" cy="-654.66" rx="51.19" ry="18"/>
<text text-anchor="middle" x="742" y="-650.96" font-family="Times-Roman" font-size="14.00">Common</text>
</g>
<!-- u0 -->
<g id="node33" class="node">
<title>u0</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="2" cx="705" cy="-576.66" rx="42.49" ry="18"/>
<text text-anchor="middle" x="705" y="-572.96" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g>
<!-- u22&#45;&gt;u0 -->
<g id="edge31" class="edge">
<title>u22&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M733.79,-636.79C729.05,-627.05 722.98,-614.59 717.64,-603.63"/>
<polygon fill="black" stroke="black" points="720.71,-601.94 713.19,-594.48 714.42,-605 720.71,-601.94"/>
</g>
<!-- u23&#45;&gt;u22 -->
<g id="edge32" class="edge">
<title>u23&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M825.95,-717.01C810.53,-705.74 788.99,-690 771.59,-677.28"/>
<polygon fill="black" stroke="black" points="773.56,-674.39 763.42,-671.31 769.43,-680.04 773.56,-674.39"/>
</g>
<!-- u24&#45;&gt;u22 -->
<g id="edge33" class="edge">
<title>u24&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M907.79,-721.35C902.96,-719.08 897.82,-716.74 893,-714.66 857.2,-699.23 815.93,-683.23 785.6,-671.79"/>
<polygon fill="black" stroke="black" points="786.75,-668.48 776.15,-668.24 784.29,-675.03 786.75,-668.48"/>
</g>
<!-- u25&#45;&gt;u0 -->
<g id="edge34" class="edge">
<title>u25&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M1167.62,-721.04C1161.48,-718.88 1155.04,-716.66 1149,-714.66 1003.04,-666.32 828.31,-614.02 748.55,-590.45"/>
<polygon fill="black" stroke="black" points="749.43,-587.06 738.85,-587.59 747.45,-593.77 749.43,-587.06"/>
</g>
<!-- u26&#45;&gt;u22 -->
<g id="edge35" class="edge">
<title>u26&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M979.97,-720.89C975.13,-718.64 969.95,-716.43 965,-714.66 892.52,-688.77 871.13,-693.36 797,-672.66 794.25,-671.89 791.42,-671.08 788.58,-670.25"/>
<polygon fill="black" stroke="black" points="789.48,-666.87 778.9,-667.37 787.48,-673.58 789.48,-666.87"/>
</g>
<!-- u27&#45;&gt;u22 -->
<g id="edge36" class="edge">
<title>u27&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M1058.23,-720.39C1051.24,-718.33 1043.92,-716.31 1037,-714.66 931.66,-689.55 902.27,-698.03 797,-672.66 794.04,-671.95 791,-671.16 787.94,-670.33"/>
<polygon fill="black" stroke="black" points="788.79,-666.93 778.21,-667.56 786.87,-673.66 788.79,-666.93"/>
</g>
<!-- u15 -->
<g id="node28" class="node">
<title>u15</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="705" cy="-380.66" rx="30.59" ry="18"/>
<text text-anchor="middle" x="705" y="-376.96" font-family="Times-Roman" font-size="14.00">Utils</text>
</g>
<!-- u28&#45;&gt;u15 -->
<g id="edge37" class="edge">
<title>u28&#45;&gt;u15</title>
<path fill="none" stroke="black" d="M71.46,-714.85C93.67,-667.18 164.47,-531.09 271,-477.66 310.47,-457.87 631.26,-485.14 668,-460.66 685.49,-449.01 694.89,-426.77 699.83,-408.86"/>
<polygon fill="black" stroke="black" points="703.29,-409.46 702.25,-398.92 696.49,-407.8 703.29,-409.46"/>
</g>
<!-- u29&#45;&gt;u22 -->
<g id="edge38" class="edge">
<title>u29&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M449.8,-720.49C456.78,-718.42 464.1,-716.37 471,-714.66 565.92,-691.09 592.14,-696.47 687,-672.66 689.96,-671.92 692.99,-671.11 696.04,-670.26"/>
<polygon fill="black" stroke="black" points="697.13,-673.6 705.77,-667.46 695.19,-666.87 697.13,-673.6"/>
</g>
<!-- u30&#45;&gt;u22 -->
<g id="edge39" class="edge">
<title>u30&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M528.08,-721.03C532.92,-718.78 538.09,-716.52 543,-714.66 605.33,-691.02 623.15,-691.84 687,-672.66 689.74,-671.84 692.55,-670.99 695.38,-670.12"/>
<polygon fill="black" stroke="black" points="696.5,-673.44 705.04,-667.17 694.46,-666.75 696.5,-673.44"/>
</g>
<!-- u31&#45;&gt;u0 -->
<g id="edge40" class="edge">
<title>u31&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M1267.21,-719.95C1204.45,-699.67 1070.24,-657.41 955,-628.66 885.44,-611.3 803.91,-595.51 753.35,-586.24"/>
<polygon fill="black" stroke="black" points="753.82,-582.77 743.35,-584.42 752.56,-589.65 753.82,-582.77"/>
</g>
<!-- u32&#45;&gt;u22 -->
<g id="edge41" class="edge">
<title>u32&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M639.9,-716.28C659.59,-704.59 686.84,-688.41 708.23,-675.71"/>
<polygon fill="black" stroke="black" points="710.2,-678.61 717.01,-670.5 706.62,-672.6 710.2,-678.61"/>
</g>
<!-- u33&#45;&gt;u22 -->
<g id="edge42" class="edge">
<title>u33&#45;&gt;u22</title>
<path fill="none" stroke="black" d="M742,-714.41C742,-705.18 742,-693.59 742,-683.15"/>
<polygon fill="black" stroke="black" points="745.5,-682.84 742,-672.84 738.5,-682.84 745.5,-682.84"/>
</g>
<!-- u34&#45;&gt;u0 -->
<g id="edge43" class="edge">
<title>u34&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M256.18,-721.27C261.01,-719.01 266.15,-716.68 271,-714.66 321.21,-693.69 336.18,-694.54 386,-672.66 424.95,-655.55 430.98,-643.09 471,-628.66 532.58,-606.45 606.93,-592.27 655.04,-584.64"/>
<polygon fill="black" stroke="black" points="655.85,-588.06 665.19,-583.07 654.78,-581.14 655.85,-588.06"/>
</g>
<!-- u35&#45;&gt;u0 -->
<g id="edge44" class="edge">
<title>u35&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M342.11,-720.95C409.78,-694.23 586.34,-624.51 665.55,-593.24"/>
<polygon fill="black" stroke="black" points="667.14,-596.37 675.16,-589.44 664.57,-589.86 667.14,-596.37"/>
</g>
<!-- u14 -->
<g id="node36" class="node">
<title>u14</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="934" cy="-495.66" rx="48.19" ry="18"/>
<text text-anchor="middle" x="934" y="-491.96" font-family="Times-Roman" font-size="14.00">Platform</text>
</g>
<!-- u36&#45;&gt;u14 -->
<g id="edge45" class="edge">
<title>u36&#45;&gt;u14</title>
<path fill="none" stroke="black" d="M1426.01,-716.06C1331.57,-673.94 1071.92,-558.16 972.98,-514.04"/>
<polygon fill="black" stroke="black" points="974.21,-510.76 963.65,-509.88 971.36,-517.15 974.21,-510.76"/>
</g>
<!-- u18 -->
<g id="node37" class="node">
<title>u18</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1461" cy="-495.66" rx="73.39" ry="18"/>
<text text-anchor="middle" x="1461" y="-491.96" font-family="Times-Roman" font-size="14.00">Requirements</text>
</g>
<!-- u36&#45;&gt;u18 -->
<g id="edge46" class="edge">
<title>u36&#45;&gt;u18</title>
<path fill="none" stroke="black" d="M1461,-714.38C1461,-673.98 1461,-573.06 1461,-524.13"/>
<polygon fill="black" stroke="black" points="1464.5,-523.97 1461,-513.97 1457.5,-523.97 1464.5,-523.97"/>
</g>
<!-- u37&#45;&gt;u0 -->
<g id="edge47" class="edge">
<title>u37&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M178.73,-719.04C219.16,-697.17 306.76,-652.29 386,-628.66 478.02,-601.22 589.65,-587.67 653.49,-581.71"/>
<polygon fill="black" stroke="black" points="654.05,-585.17 663.7,-580.78 653.42,-578.2 654.05,-585.17"/>
</g> </g>
<!-- u3 --> <!-- u3 -->
<g id="node21" class="node"> <g id="node4" class="node">
<title>u3</title> <title>u3</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="1257" cy="-213.66" rx="36.29" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="534.39" cy="-197" rx="36.29" ry="18"/>
<text text-anchor="middle" x="1257" y="-209.96" font-family="Times-Roman" font-size="14.00">Types</text> <text text-anchor="middle" x="534.39" y="-193.3" font-family="Times-Roman" font-size="14.00">Types</text>
</g> </g>
<!-- u4 --> <!-- u4 -->
<g id="node22" class="node"> <g id="node5" class="node">
<title>u4</title> <title>u4</title>
<ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="1329" cy="-291.66" rx="39.79" ry="18"/> <ellipse fill="#ffbbbb" stroke="black" stroke-width="0" cx="427.39" cy="-269" rx="39.79" ry="18"/>
<text text-anchor="middle" x="1329" y="-287.96" font-family="Times-Roman" font-size="14.00">Optics</text> <text text-anchor="middle" x="427.39" y="-265.3" font-family="Times-Roman" font-size="14.00">Optics</text>
</g> </g>
<!-- u4&#45;&gt;u3 --> <!-- u4&#45;&gt;u3 -->
<g id="edge48" class="edge"> <g id="edge18" class="edge">
<title>u4&#45;&gt;u3</title> <title>u4&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1314.08,-274.91C1303.94,-264.21 1290.37,-249.88 1278.99,-237.88"/> <path fill="none" stroke="black" d="M449.04,-253.83C464.95,-243.42 486.78,-229.15 504.37,-217.64"/>
<polygon fill="black" stroke="black" points="1281.32,-235.24 1271.9,-230.39 1276.24,-240.05 1281.32,-235.24"/> <polygon fill="black" stroke="black" points="506.68,-220.31 513.13,-211.91 502.85,-214.45 506.68,-220.31"/>
</g> </g>
<!-- u6 --> <!-- u6 -->
<g id="node29" class="node"> <g id="node12" class="node">
<title>u6</title> <title>u6</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="934" cy="-380.66" rx="64.19" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="855.39" cy="-435" rx="64.19" ry="18"/>
<text text-anchor="middle" x="934" y="-376.96" font-family="Times-Roman" font-size="14.00">MegaParsec</text> <text text-anchor="middle" x="855.39" y="-431.3" font-family="Times-Roman" font-size="14.00">MegaParsec</text>
</g> </g>
<!-- u5&#45;&gt;u6 --> <!-- u5&#45;&gt;u6 -->
<g id="edge49" class="edge"> <g id="edge19" class="edge">
<title>u5&#45;&gt;u6</title> <title>u5&#45;&gt;u6</title>
<path fill="none" stroke="black" d="M1226.6,-309.05C1217.94,-320.06 1204.99,-333.77 1190,-340.66 1152.94,-357.69 1046.95,-346.43 1007,-354.66 997.85,-356.55 988.26,-359.26 979.16,-362.21"/> <path fill="none" stroke="black" d="M566.7,-284.68C579.52,-295.01 597.75,-308.62 615.39,-318 624.58,-322.89 630.09,-318.59 637.39,-326 662.15,-351.13 635.32,-379.63 663.39,-401 674.37,-409.36 773.88,-406.14 787.39,-409 795.7,-410.76 804.37,-413.33 812.61,-416.16"/>
<polygon fill="black" stroke="black" points="977.86,-358.96 969.51,-365.48 980.11,-365.59 977.86,-358.96"/> <polygon fill="black" stroke="black" points="811.46,-419.47 822.06,-419.57 813.84,-412.88 811.46,-419.47"/>
</g> </g>
<!-- u7 --> <!-- u7 -->
<g id="node30" class="node"> <g id="node13" class="node">
<title>u7</title> <title>u7</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1030" cy="-213.66" rx="44.39" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="828.39" cy="-197" rx="44.39" ry="18"/>
<text text-anchor="middle" x="1030" y="-209.96" font-family="Times-Roman" font-size="14.00">Prelude</text> <text text-anchor="middle" x="828.39" y="-193.3" font-family="Times-Roman" font-size="14.00">Prelude</text>
</g> </g>
<!-- u5&#45;&gt;u7 --> <!-- u5&#45;&gt;u7 -->
<g id="edge50" class="edge"> <g id="edge20" class="edge">
<title>u5&#45;&gt;u7</title> <title>u5&#45;&gt;u7</title>
<path fill="none" stroke="black" d="M1218.91,-276.55C1213.05,-272.65 1206.44,-268.67 1200,-265.66 1160.61,-247.27 1113.01,-233.67 1078.21,-225.15"/> <path fill="none" stroke="black" d="M567.9,-253.71C573.85,-249.71 580.65,-245.72 587.39,-243 648.94,-218.19 724.91,-206.91 774.9,-201.88"/>
<polygon fill="black" stroke="black" points="1078.83,-221.7 1068.29,-222.78 1077.21,-228.51 1078.83,-221.7"/> <polygon fill="black" stroke="black" points="775.54,-205.33 785.16,-200.9 774.87,-198.36 775.54,-205.33"/>
</g> </g>
<!-- u9 --> <!-- u9 -->
<g id="node25" class="node"> <g id="node8" class="node">
<title>u9</title> <title>u9</title>
<ellipse fill="#77ff77" stroke="black" stroke-width="0" cx="803" cy="-291.66" rx="51.19" ry="18"/> <ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="936.39" cy="-269" rx="51.19" ry="18"/>
<text text-anchor="middle" x="803" y="-287.96" font-family="Times-Roman" font-size="14.00">Common</text> <text text-anchor="middle" x="936.39" y="-265.3" font-family="Times-Roman" font-size="14.00">Common</text>
</g> </g>
<!-- u12&#45;&gt;u9 --> <!-- u12&#45;&gt;u9 -->
<g id="edge59" class="edge"> <g id="edge30" class="edge">
<title>u12&#45;&gt;u9</title> <title>u12&#45;&gt;u9</title>
<path fill="none" stroke="black" d="M820.65,-362.47C817.58,-350.32 813.4,-333.77 809.87,-319.84"/> <path fill="none" stroke="black" d="M925.36,-333.82C927.2,-323.19 929.6,-309.31 931.69,-297.2"/>
<polygon fill="black" stroke="black" points="813.18,-318.65 807.34,-309.81 806.4,-320.37 813.18,-318.65"/> <polygon fill="black" stroke="black" points="935.17,-297.6 933.42,-287.15 928.27,-296.41 935.17,-297.6"/>
</g> </g>
<!-- u9&#45;&gt;u7 --> <!-- u9&#45;&gt;u7 -->
<g id="edge60" class="edge"> <g id="edge31" class="edge">
<title>u9&#45;&gt;u7</title> <title>u9&#45;&gt;u7</title>
<path fill="none" stroke="black" d="M831.8,-276.66C840.15,-272.85 849.35,-268.89 858,-265.66 899.83,-250.04 948.74,-235.92 983.69,-226.52"/> <path fill="none" stroke="black" d="M912.96,-252.81C897.32,-242.68 876.58,-229.24 859.56,-218.2"/>
<polygon fill="black" stroke="black" points="984.85,-229.83 993.61,-223.88 983.05,-223.07 984.85,-229.83"/> <polygon fill="black" stroke="black" points="861.33,-215.18 851.04,-212.68 857.53,-221.06 861.33,-215.18"/>
</g> </g>
<!-- u10 --> <!-- u10 -->
<g id="node26" class="node"> <g id="node9" class="node">
<title>u10</title> <title>u10</title>
<ellipse fill="#ffff77" stroke="black" stroke-width="0" cx="1030" cy="-53.66" rx="27" ry="18"/> <ellipse fill="#77ff77" stroke="black" stroke-width="0" cx="828.39" cy="-50" rx="27" ry="18"/>
<text text-anchor="middle" x="1030" y="-49.96" font-family="Times-Roman" font-size="14.00">QQ</text> <text text-anchor="middle" x="828.39" y="-46.3" font-family="Times-Roman" font-size="14.00">QQ</text>
</g> </g>
<!-- u16 --> <!-- u16 -->
<g id="node27" class="node"> <g id="node10" class="node">
<title>u16</title> <title>u16</title>
<ellipse fill="#7777ff" stroke="black" stroke-width="0" cx="629" cy="-380.66" rx="27" ry="18"/> <ellipse fill="#ffff77" stroke="black" stroke-width="0" cx="1001.39" cy="-435" rx="27" ry="18"/>
<text text-anchor="middle" x="629" y="-376.96" font-family="Times-Roman" font-size="14.00">QQ</text> <text text-anchor="middle" x="1001.39" y="-431.3" font-family="Times-Roman" font-size="14.00">QQ</text>
</g> </g>
<!-- u15&#45;&gt;u1 --> <!-- u15 -->
<g id="edge51" class="edge"> <g id="node11" class="node">
<title>u15&#45;&gt;u1</title> <title>u15</title>
<path fill="none" stroke="black" d="M707.32,-398.87C710.83,-417.56 719.45,-446.6 740,-460.66 773.6,-483.65 1065.83,-471.03 1106,-477.66 1109.69,-478.27 1113.47,-479.03 1117.26,-479.89"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="2" cx="1103.39" cy="-435" rx="30.59" ry="18"/>
<polygon fill="black" stroke="black" points="1116.67,-483.35 1127.21,-482.36 1118.35,-476.55 1116.67,-483.35"/> <text text-anchor="middle" x="1103.39" y="-431.3" font-family="Times-Roman" font-size="14.00">Utils</text>
</g>
<!-- u15&#45;&gt;u12 -->
<g id="edge22" class="edge">
<title>u15&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M1080.91,-422.68C1072.07,-418.31 1061.8,-413.33 1052.39,-409 1019.33,-393.79 981.02,-377.44 954.59,-366.35"/>
<polygon fill="black" stroke="black" points="955.66,-363.01 945.08,-362.38 952.96,-369.47 955.66,-363.01"/>
</g>
<!-- u15&#45;&gt;u11 -->
<g id="edge21" class="edge">
<title>u15&#45;&gt;u11</title>
<path fill="none" stroke="black" d="M1082.81,-421.48C1073.8,-416.65 1062.9,-411.66 1052.39,-409 1009.49,-398.14 893.64,-419.41 853.39,-401 842.1,-395.84 832.28,-386.4 824.72,-377.25"/>
<polygon fill="black" stroke="black" points="827.3,-374.87 818.44,-369.07 821.75,-379.13 827.3,-374.87"/>
</g> </g>
<!-- u6&#45;&gt;u3 --> <!-- u6&#45;&gt;u3 -->
<g id="edge52" class="edge"> <g id="edge23" class="edge">
<title>u6&#45;&gt;u3</title> <title>u6&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M969.52,-365.51C981.28,-361.37 994.54,-357.25 1007,-354.66 1044.76,-346.8 1151.18,-366.3 1180,-340.66 1205.4,-318.06 1177.53,-294.82 1195,-265.66 1202.54,-253.08 1214.35,-242.23 1225.65,-233.81"/> <path fill="none" stroke="black" d="M817.86,-420.3C799.34,-413.36 779.12,-405.48 770.39,-401 716.1,-373.14 711.12,-350.84 655.39,-326 642.79,-320.38 635.87,-326.98 625.39,-318 597.74,-294.3 614.8,-271.71 592.39,-243 584.81,-233.29 574.68,-224.49 565.01,-217.31"/>
<polygon fill="black" stroke="black" points="1227.95,-236.47 1234.11,-227.86 1223.92,-230.75 1227.95,-236.47"/> <polygon fill="black" stroke="black" points="566.89,-214.35 556.71,-211.43 562.85,-220.06 566.89,-214.35"/>
</g> </g>
<!-- u8 --> <!-- u8 -->
<g id="node31" class="node"> <g id="node14" class="node">
<title>u8</title> <title>u8</title>
<ellipse fill="#ffbbff" stroke="black" stroke-width="0" cx="1030" cy="-135.66" rx="42.49" ry="18"/> <ellipse fill="#bbffff" stroke="black" stroke-width="0" cx="828.39" cy="-125" rx="42.49" ry="18"/>
<text text-anchor="middle" x="1030" y="-131.96" font-family="Times-Roman" font-size="14.00">Logger</text> <text text-anchor="middle" x="828.39" y="-121.3" font-family="Times-Roman" font-size="14.00">Logger</text>
</g> </g>
<!-- u7&#45;&gt;u8 --> <!-- u7&#45;&gt;u8 -->
<g id="edge54" class="edge"> <g id="edge25" class="edge">
<title>u7&#45;&gt;u8</title> <title>u7&#45;&gt;u8</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1030,-195.41C1030,-186.18 1030,-174.59 1030,-164.15"/> <path fill="none" stroke="black" stroke-dasharray="5,2" d="M828.39,-178.7C828.39,-170.98 828.39,-161.71 828.39,-153.11"/>
<polygon fill="black" stroke="black" points="1033.5,-163.84 1030,-153.84 1026.5,-163.84 1033.5,-163.84"/> <polygon fill="black" stroke="black" points="831.89,-153.1 828.39,-143.1 824.89,-153.1 831.89,-153.1"/>
</g> </g>
<!-- u2 --> <!-- u2 -->
<g id="node34" class="node"> <g id="node17" class="node">
<title>u2</title> <title>u2</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="1128" cy="-291.66" rx="37.89" ry="18"/> <ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="696.39" cy="-269" rx="37.89" ry="18"/>
<text text-anchor="middle" x="1128" y="-287.96" font-family="Times-Roman" font-size="14.00">Errors</text> <text text-anchor="middle" x="696.39" y="-265.3" font-family="Times-Roman" font-size="14.00">Errors</text>
</g> </g>
<!-- u7&#45;&gt;u2 --> <!-- u7&#45;&gt;u2 -->
<g id="edge53" class="edge"> <g id="edge24" class="edge">
<title>u7&#45;&gt;u2</title> <title>u7&#45;&gt;u2</title>
<path fill="none" stroke="black" d="M1049.85,-230.05C1064.58,-241.48 1084.85,-257.2 1101.05,-269.76"/> <path fill="none" stroke="black" d="M802.53,-211.71C781.65,-222.79 752.12,-238.45 729.51,-250.44"/>
<polygon fill="black" stroke="black" points="1099.06,-272.64 1109.1,-276.01 1103.35,-267.11 1099.06,-272.64"/> <polygon fill="black" stroke="black" points="727.8,-247.38 720.6,-255.16 731.07,-253.57 727.8,-247.38"/>
</g> </g>
<!-- u8&#45;&gt;u4 --> <!-- u8&#45;&gt;u4 -->
<g id="edge55" class="edge"> <g id="edge26" class="edge">
<title>u8&#45;&gt;u4</title> <title>u8&#45;&gt;u4</title>
<path fill="none" stroke="black" d="M1071.74,-139.12C1139.49,-143.98 1269.17,-157.08 1302,-187.66 1322.76,-207 1328.33,-240.18 1329.47,-263.64"/> <path fill="none" stroke="black" d="M786.3,-127.22C706.73,-130.23 537.84,-140.15 489.39,-171 463.54,-187.46 446.42,-218.99 436.84,-241.64"/>
<polygon fill="black" stroke="black" points="1325.97,-263.75 1329.71,-273.66 1332.97,-263.58 1325.97,-263.75"/> <polygon fill="black" stroke="black" points="433.54,-240.45 433.08,-251.04 440.04,-243.06 433.54,-240.45"/>
</g> </g>
<!-- u8&#45;&gt;u9 --> <!-- u8&#45;&gt;u9 -->
<g id="edge56" class="edge"> <g id="edge27" class="edge">
<title>u8&#45;&gt;u9</title> <title>u8&#45;&gt;u9</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1008.49,-151.25C968.13,-178.63 880.88,-237.82 834.11,-269.55"/> <path fill="none" stroke="black" stroke-dasharray="5,2" d="M849.03,-140.76C859.52,-148.9 872,-159.65 881.39,-171 899.5,-192.89 914.92,-221.56 924.79,-242.04"/>
<polygon fill="black" stroke="black" points="831.91,-266.82 825.6,-275.33 835.84,-272.61 831.91,-266.82"/> <polygon fill="black" stroke="black" points="921.62,-243.54 929.04,-251.1 927.96,-240.56 921.62,-243.54"/>
</g> </g>
<!-- u8&#45;&gt;u10 --> <!-- u8&#45;&gt;u10 -->
<g id="edge57" class="edge"> <g id="edge28" class="edge">
<title>u8&#45;&gt;u10</title> <title>u8&#45;&gt;u10</title>
<path fill="none" stroke="black" d="M1030,-117.3C1030,-106.96 1030,-93.6 1030,-81.88"/> <path fill="none" stroke="black" d="M828.39,-106.7C828.39,-98.25 828.39,-87.87 828.39,-78.37"/>
<polygon fill="black" stroke="black" points="1033.5,-81.71 1030,-71.71 1026.5,-81.71 1033.5,-81.71"/> <polygon fill="black" stroke="black" points="831.89,-78.18 828.39,-68.18 824.89,-78.18 831.89,-78.18"/>
</g> </g>
<!-- u11&#45;&gt;u5 --> <!-- u11&#45;&gt;u5 -->
<g id="edge58" class="edge"> <g id="edge29" class="edge">
<title>u11&#45;&gt;u5</title> <title>u11&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1060.4,-365.35C1065.63,-361.25 1071.74,-357.21 1078,-354.66 1124.47,-335.76 1144.94,-362.7 1190,-340.66 1201.46,-335.05 1211.82,-325.6 1219.95,-316.56"/> <path fill="none" stroke="black" d="M781.07,-346.57C749.06,-341.43 694.42,-332.79 647.39,-326 620.76,-322.16 610.78,-331.29 587.39,-318 577.83,-312.56 569.78,-303.68 563.6,-295.02"/>
<polygon fill="black" stroke="black" points="1222.7,-318.72 1226.49,-308.83 1217.36,-314.2 1222.7,-318.72"/> <polygon fill="black" stroke="black" points="566.46,-293 558.04,-286.57 560.61,-296.85 566.46,-293"/>
</g>
<!-- u0 -->
<g id="node16" class="node">
<title>u0</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="2" cx="840.39" cy="-541" rx="42.49" ry="18"/>
<text text-anchor="middle" x="840.39" y="-537.3" font-family="Times-Roman" font-size="14.00">GHCup</text>
</g>
<!-- u0&#45;&gt;u1 -->
<g id="edge4" class="edge">
<title>u0&#45;&gt;u1</title>
<path fill="none" stroke="black" d="M798.15,-540.08C733.62,-539.53 614.12,-535.65 577.39,-515 556.17,-503.07 540.32,-480.02 530.42,-461.88"/>
<polygon fill="black" stroke="black" points="533.49,-460.2 525.79,-452.93 527.27,-463.42 533.49,-460.2"/>
</g> </g>
<!-- u0&#45;&gt;u16 --> <!-- u0&#45;&gt;u16 -->
<g id="edge4" class="edge"> <g id="edge6" class="edge">
<title>u0&#45;&gt;u16</title> <title>u0&#45;&gt;u16</title>
<path fill="none" stroke="black" d="M698.39,-558.78C685.1,-524.87 655.09,-448.27 639.25,-407.82"/> <path fill="none" stroke="black" d="M881.32,-536.43C914.21,-532.8 957.04,-526.09 970.39,-515 985.87,-502.13 993.66,-480.49 997.55,-463.11"/>
<polygon fill="black" stroke="black" points="642.45,-406.39 635.54,-398.36 635.93,-408.95 642.45,-406.39"/> <polygon fill="black" stroke="black" points="1001.06,-463.44 999.51,-452.96 994.18,-462.12 1001.06,-463.44"/>
</g> </g>
<!-- u0&#45;&gt;u15 --> <!-- u0&#45;&gt;u15 -->
<g id="edge3" class="edge"> <g id="edge5" class="edge">
<title>u0&#45;&gt;u15</title> <title>u0&#45;&gt;u15</title>
<path fill="none" stroke="black" d="M705,-558.44C705,-524.72 705,-449.72 705,-409.09"/> <path fill="none" stroke="black" d="M882.83,-539.21C936.42,-537.51 1024.95,-532.11 1052.39,-515 1071.91,-502.83 1085.39,-480.12 1093.57,-462.17"/>
<polygon fill="black" stroke="black" points="708.5,-408.84 705,-398.84 701.5,-408.84 708.5,-408.84"/> <polygon fill="black" stroke="black" points="1096.87,-463.35 1097.57,-452.78 1090.43,-460.6 1096.87,-463.35"/>
</g>
<!-- u14 -->
<g id="node19" class="node">
<title>u14</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="680.39" cy="-435" rx="48.19" ry="18"/>
<text text-anchor="middle" x="680.39" y="-431.3" font-family="Times-Roman" font-size="14.00">Platform</text>
</g> </g>
<!-- u0&#45;&gt;u14 --> <!-- u0&#45;&gt;u14 -->
<g id="edge2" class="edge"> <g id="edge3" class="edge">
<title>u0&#45;&gt;u14</title> <title>u0&#45;&gt;u14</title>
<path fill="none" stroke="black" d="M736.84,-564.68C776.8,-550.89 845.52,-527.18 890.35,-511.72"/> <path fill="none" stroke="black" d="M801.74,-533.4C786.24,-529.61 768.7,-523.78 754.39,-515 731.68,-501.06 711.42,-478.33 697.87,-460.82"/>
<polygon fill="black" stroke="black" points="891.51,-515.02 899.82,-508.45 889.22,-508.4 891.51,-515.02"/> <polygon fill="black" stroke="black" points="700.58,-458.59 691.78,-452.7 694.98,-462.8 700.58,-458.59"/>
</g> </g>
<!-- u2&#45;&gt;u3 --> <!-- u2&#45;&gt;u3 -->
<g id="edge5" class="edge"> <g id="edge7" class="edge">
<title>u2&#45;&gt;u3</title> <title>u2&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1150.77,-277.25C1171.58,-264.98 1202.56,-246.73 1225.7,-233.1"/> <path fill="none" stroke="black" d="M669.27,-256.28C642.05,-244.52 599.95,-226.33 570.04,-213.4"/>
<polygon fill="black" stroke="black" points="1227.62,-236.03 1234.46,-227.94 1224.07,-230 1227.62,-236.03"/> <polygon fill="black" stroke="black" points="571.35,-210.16 560.79,-209.41 568.58,-216.59 571.35,-210.16"/>
</g> </g>
<!-- u13&#45;&gt;u3 --> <!-- u13&#45;&gt;u3 -->
<g id="edge6" class="edge"> <g id="edge8" class="edge">
<title>u13&#45;&gt;u3</title> <title>u13&#45;&gt;u3</title>
<path fill="none" stroke="black" d="M1412.98,-362.38C1410.65,-337.92 1403.02,-293.12 1378,-265.66 1357.81,-243.49 1326.42,-230.64 1300.67,-223.38"/> <path fill="none" stroke="black" d="M186.09,-260.84C259.14,-247.66 414.32,-219.66 490.43,-205.93"/>
<polygon fill="black" stroke="black" points="1301.32,-219.94 1290.76,-220.78 1299.54,-226.71 1301.32,-219.94"/> <polygon fill="black" stroke="black" points="491.41,-209.31 500.63,-204.09 490.17,-202.42 491.41,-209.31"/>
</g> </g>
<!-- u14&#45;&gt;u5 --> <!-- u14&#45;&gt;u5 -->
<g id="edge7" class="edge"> <g id="edge9" class="edge">
<title>u14&#45;&gt;u5</title> <title>u14&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M980.15,-490.19C1019.57,-485.37 1072.04,-476.2 1086,-460.66 1117.68,-425.38 1065.12,-388.82 1098,-354.66 1126.68,-324.86 1153.09,-359.32 1190,-340.66 1201.39,-334.9 1211.73,-325.43 1219.87,-316.41"/> <path fill="none" stroke="black" d="M656.5,-419.22C649.64,-414.1 642.62,-407.9 637.39,-401 615.71,-372.38 627.41,-355.8 607.39,-326 598.66,-313.01 586.48,-300.68 575.54,-290.91"/>
<polygon fill="black" stroke="black" points="1222.61,-318.59 1226.43,-308.71 1217.28,-314.05 1222.61,-318.59"/> <polygon fill="black" stroke="black" points="577.72,-288.16 567.86,-284.28 573.14,-293.46 577.72,-288.16"/>
</g> </g>
<!-- u14&#45;&gt;u12 --> <!-- u14&#45;&gt;u12 -->
<g id="edge8" class="edge"> <g id="edge10" class="edge">
<title>u14&#45;&gt;u12</title> <title>u14&#45;&gt;u12</title>
<path fill="none" stroke="black" d="M895.43,-484.64C881.53,-479.41 866.66,-471.72 856,-460.66 842.29,-446.43 834.43,-425.42 830.06,-408.64"/> <path fill="none" stroke="black" d="M711.94,-421.27C724.88,-416.59 740.15,-411.77 754.39,-409 784.33,-403.18 865.04,-416.36 891.39,-401 900.07,-395.94 906.74,-387.36 911.63,-378.84"/>
<polygon fill="black" stroke="black" points="833.46,-407.78 827.77,-398.84 826.64,-409.37 833.46,-407.78"/> <polygon fill="black" stroke="black" points="914.82,-380.3 916.24,-369.8 908.58,-377.12 914.82,-380.3"/>
</g>
<!-- u18 -->
<g id="node20" class="node">
<title>u18</title>
<ellipse fill="#ffffbb" stroke="black" stroke-width="0" cx="146.39" cy="-352" rx="73.39" ry="18"/>
<text text-anchor="middle" x="146.39" y="-348.3" font-family="Times-Roman" font-size="14.00">Requirements</text>
</g> </g>
<!-- u18&#45;&gt;u5 --> <!-- u18&#45;&gt;u5 -->
<g id="edge10" class="edge"> <g id="edge12" class="edge">
<title>u18&#45;&gt;u5</title> <title>u18&#45;&gt;u5</title>
<path fill="none" stroke="black" d="M1468.63,-477.37C1480.29,-447.97 1498.11,-388.19 1467,-354.66 1438.65,-324.11 1317.28,-359.28 1280,-340.66 1269.49,-335.41 1260.48,-326.27 1253.53,-317.36"/> <path fill="none" stroke="black" d="M192.35,-337.85C210.3,-333.27 231.16,-328.62 250.39,-326 300.18,-319.2 428.51,-333.26 476.39,-318 493.93,-312.41 511.26,-301.32 524.62,-291.22"/>
<polygon fill="black" stroke="black" points="1256.2,-315.09 1247.51,-309.03 1250.53,-319.18 1256.2,-315.09"/> <polygon fill="black" stroke="black" points="527.1,-293.72 532.79,-284.78 522.77,-288.22 527.1,-293.72"/>
</g> </g>
<!-- u18&#45;&gt;u13 --> <!-- u18&#45;&gt;u13 -->
<g id="edge9" class="edge"> <g id="edge11" class="edge">
<title>u18&#45;&gt;u13</title> <title>u18&#45;&gt;u13</title>
<path fill="none" stroke="black" d="M1453.86,-477.5C1446.19,-459.06 1433.9,-429.51 1425,-408.1"/> <path fill="none" stroke="black" d="M146.39,-333.82C146.39,-323.19 146.39,-309.31 146.39,-297.2"/>
<polygon fill="black" stroke="black" points="1428.12,-406.5 1421.05,-398.61 1421.66,-409.19 1428.12,-406.5"/> <polygon fill="black" stroke="black" points="149.89,-297.15 146.39,-287.15 142.89,-297.15 149.89,-297.15"/>
</g> </g>
<!-- u20 --> <!-- u20 -->
<g id="node38" class="node"> <g id="node21" class="node">
<title>u20</title> <title>u20</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="742" cy="-925.66" rx="32.49" ry="18"/> <ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="840.39" cy="-688" rx="32.49" ry="18"/>
<text text-anchor="middle" x="742" y="-921.96" font-family="Times-Roman" font-size="14.00">Main</text> <text text-anchor="middle" x="840.39" y="-684.3" font-family="Times-Roman" font-size="14.00">Main</text>
</g>
<!-- u21 -->
<g id="node22" class="node">
<title>u21</title>
<ellipse fill="#bbffbb" stroke="black" stroke-width="0" cx="840.39" cy="-616" rx="46.29" ry="18"/>
<text text-anchor="middle" x="840.39" y="-612.3" font-family="Times-Roman" font-size="14.00">Validate</text>
</g> </g>
<!-- u20&#45;&gt;u21 --> <!-- u20&#45;&gt;u21 -->
<g id="edge1" class="edge"> <g id="edge1" class="edge">
<title>u20&#45;&gt;u21</title> <title>u20&#45;&gt;u21</title>
<path fill="none" stroke="black" d="M742,-907.5C742,-889.33 742,-860.38 742,-839.05"/> <path fill="none" stroke="black" d="M840.39,-669.7C840.39,-661.98 840.39,-652.71 840.39,-644.11"/>
<polygon fill="black" stroke="black" points="745.5,-838.98 742,-828.98 738.5,-838.98 745.5,-838.98"/> <polygon fill="black" stroke="black" points="843.89,-644.1 840.39,-634.1 836.89,-644.1 843.89,-644.1"/>
</g>
<!-- u21&#45;&gt;u0 -->
<g id="edge2" class="edge">
<title>u21&#45;&gt;u0</title>
<path fill="none" stroke="black" d="M840.39,-597.7C840.39,-589.25 840.39,-578.87 840.39,-569.37"/>
<polygon fill="black" stroke="black" points="843.89,-569.18 840.39,-559.18 836.89,-569.18 843.89,-569.18"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,349 +0,0 @@
# First steps
In this guide we'll take a look at a few core tools that are installed
with the Haskell toolchain, namely, `ghc`, `runghc` and `ghci`.
These tools can be used to compile, interpret or explore Haskell programs.
First, let's start by opening your system's command line interface
and running `ghc --version` to make sure we have successfully
installed a Haskell toolchain:
```
➜ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.10.7
```
If this fails, consult [the Getting started page](../install) for information on
how to install Haskell on your computer.
This guide is partly based on [Gil Mizrahi's blog](https://gilmi.me/blog/post/2021/08/14/hs-core-tools).
## Compiling programs with ghc
Running `ghc` invokes the Glasgow Haskell Compiler (GHC), and can be used to
compile Haskell modules and programs into native executables and libraries.
Create a new Haskell source file named `hello.hs`,
and write the following code in it:
```hs
main = putStrLn "Hello, Haskell!"
```
Now, we can compile the program by invoking `ghc` with the file name:
```sh
➜ ghc hello.hs
[1 of 1] Compiling Main ( hello.hs, hello.o )
Linking hello ...
```
For more in-depth information about the files `ghc` produces,
follow the [GHC user guide](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using.html#getting-started-compiling-programs) guide.
Now we run our program:
```sh
➜ ./hello
Hello, Haskell!
```
Alternatively, we can skip the compilation phase by using the command `runghc`:
```sh
➜ runghc hello.hs
Hello, Haskell!
```
`runghc` interprets the source file instead of compiling it and does not
create build artifacts. This makes it very useful when developing programs
and can help accelerate the feedback loop. More information about `runghc`
can be found in the
[GHC user guide](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/runghc.html).
### Turning on warnings
The `-Wall` flag will enable GHC to emit warnings about our code.
```sh
➜ ghc -Wall hello.hs -fforce-recomp
[1 of 1] Compiling Main ( hello.hs, hello.o )
hello.hs:1:1: warning: [-Wmissing-signatures]
Top-level binding with no type signature: main :: IO ()
|
1 | main = putStrLn "Hello, Haskell!"
| ^^^^
Linking hello ...
```
While Haskell can infer
the types of most expressions, it is recommended that top-level definitions
are annotated with their types.
Now our `hello.hs` source file should looks like this:
```hs
main :: IO ()
main = putStrLn "Hello, world!"
```
And now GHC will compile `hello.hs` without warnings.
## An interactive environment
GHC provides an interactive environment in a form of a
Read-Evaluate-Print Loop (REPL) called GHCi.
To enter the environment run the program `ghci`.
```sh
➜ ghci
GHCi, version 9.0.2: https://www.haskell.org/ghc/ :? for help
ghci>
```
It provides an interactive prompt where Haskell expressions can be written and
evaluated.
For example:
```sh
ghci> 1 + 1
2
ghci> putStrLn "Hello, world!"
Hello, world!
```
We can define new names:
```sh
ghci> double x = x + x
ghci> double 2
4
```
We can write multi-line code by surrounding it with `:{` and `:}`:
```hs
ghci> :{
| map f list =
| case list of
| [] -> []
| x : xs -> f x : map f xs
| :}
ghci> map (+1) [1, 2, 3]
[2,3,4]
```
We can import Haskell source files using the `:load` command (`:l` for short):
```sh
ghci> :load hello.hs
[1 of 1] Compiling Main ( hello.hs, interpreted )
Ok, one module loaded.
ghci> main
Hello, Haskell!
```
As well as import library modules:
```sh
ghci> import Data.Bits
ghci> shiftL 32 1
64
ghci> clearBit 33 0
32
```
We can even ask what the type of an expression is using the `:type` command
(`:t` for short):
```sh
λ> :type putStrLn
putStrLn :: String -> IO ()
```
To exit `ghci`, use the `:quit` command (or `:q` for short)
```sh
ghci> :quit
Leaving GHCi.
```
A more thorough introduction to GHCi can be found in the
[GHC user guide](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html).
### Using external packages in ghci
By default, GHCi can only load and use packages that are
[included with the GHC installation](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/9.2.2-notes.html#included-libraries).
However, users of the [cabal-install](https://www.haskell.org/cabal) and
[stack](http://haskellstack.org) build tools can download and load external packages
very easily using the following commands:
cabal-install:
```sh
cabal repl --build-depends async,say
```
Stack:
```sh
stack exec --package async --package say -- ghci
```
And the modules of the relevant packages will be available for import:
```sh
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
ghci> import Control.Concurrent.Async
ghci> import Say
ghci> concurrently_ (sayString "Hello") (sayString "World")
Hello
World
```
Stack users can also use this feature with `runghc` and `ghc` by replacing
`ghci` in the command above, and cabal-install users can generate an
environment file that will make `async` and `say` visible for GHC tools
in the current directory using this command:
```sh
cabal install --lib async say --package-env .
```
Many more packages are waiting for you on [Hackage](https://hackage.haskell.org).
## Creating a proper package with modules
The previous methods to compile Haskell code are for quick experiments and small
programs. Usually in Haskell, we create cabal projects, where build tools such as
`cabal-install` or `stack` will install necessary dependencies and compile modules
in correct order. For simplicity's sake, this section will only use `cabal-install`.
To get started, run:
```sh
mkdir haskell-project
cd haskell-project
cabal init --interactive
```
If you let it generate a simple project with sensible defaults, then you should have these files:
* `src/MyLib.hs`: the library module of your project
* `app/Main.hs`: the entry point of your project
* `haskell-project.cabal`: the "cabal" file, describing your project, its dependencies and how it's built
To build the project, run:
```sh
cabal build
```
To run the main executable, run:
```sh
➜ cabal run
Hello, Haskell!
someFunc
```
### Adding dependencies
Now let's add a dependency and adjust our library module. Open `haskell-project.cabal`
and find the library section:
```
library
exposed-modules: MyLib
-- Modules included in this library but not exported.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends: base ^>=4.14.3.0
hs-source-dirs: src
default-language: Haskell2010
```
The interesting parts here are `exposed-modules` and `build-depends`.
To add a dependency, it should look like this:
```
build-depends: base ^>=4.14.3.0
, directory
```
Now open `src/MyLib.hs` and change it to:
```hs
module MyLib (someFunc) where
import System.Directory
someFunc :: IO ()
someFunc = do
contents <- listDirectory "src"
putStrLn (show contents)
```
### Adding modules
To add a module to your package, adjust `exposed-modules`, like so
```
exposed-modules: MyLib
OtherLib
```
then create `src/OtherLib.hs` with the following contents:
```hs
module OtherLib where
otherFunc :: String -> Int
otherFunc str = length str
```
To use this function interactively, we can run:
```sh
➜ cabal repl
ghci> import OtherLib
ghci> otherFunc "Hello Haskell"
13
```
For further information about how to manage Haskell projects
see the [Cabal user guide](https://cabal.readthedocs.io/en/stable/getting-started.html).
# Where to go from here
<div class="text-center main-buttons">
<a href="https://hackage.haskell.org/" class="btn btn-primary" role="button">Discover Haskell packages</a>
<a href="https://hackage.haskell.org/package/base" class="btn btn-primary" role="button">The standard library</a>
<a href="https://haskell-language-server.readthedocs.io/en/stable/installation.html" class="btn btn-primary" role="button">Editor setup with HLS</a>
<a href="https://play-haskell.tomsmeding.com/play" class="btn btn-primary" role="button">Online playground</a>
</div>
## How to learn Haskell proper
To learn Haskell, try any of those:
- A beginner friendly [4-lectures course](https://github.com/haskell-beginners-2022/course-plan) with exercises (by [Kowainik](https://kowainik.github.io/))
- An in-depth university [CIS 194 Haskell course](https://www.cis.upenn.edu/~cis194/spring13/) including exercises (by [Brent Yorgey](https://byorgey.wordpress.com/))
## Projects to contribute to
* [https://github.com/haskell/haskell-language-server](https://github.com/haskell/haskell-language-server)
* [https://github.com/haskell/cabal](https://github.com/haskell/cabal)
* [https://github.com/commercialhaskell/stack](https://github.com/commercialhaskell/stack)
* [https://gitlab.haskell.org/haskell/ghcup-hs](https://gitlab.haskell.org/haskell/ghcup-hs)
* [https://github.com/jgm/pandoc](https://github.com/jgm/pandoc)
* [https://github.com/simonmichael/hledger](https://github.com/simonmichael/hledger)
* [https://github.com/koalaman/shellcheck](https://github.com/koalaman/shellcheck)

View File

@@ -1,6 +1,6 @@
cabal-version: 3.0 cabal-version: 3.0
name: ghcup name: ghcup
version: 0.1.18.0 version: 0.1.17.2
license: LGPL-3.0-only license: LGPL-3.0-only
license-file: LICENSE license-file: LICENSE
copyright: Julian Ospald 2020 copyright: Julian Ospald 2020
@@ -16,8 +16,11 @@ description:
category: System category: System
build-type: Simple build-type: Simple
extra-doc-files: extra-doc-files:
CHANGELOG.md
data/config.yaml data/config.yaml
data/metadata/ghcup-0.0.4.yaml
data/metadata/ghcup-0.0.5.yaml
data/metadata/ghcup-0.0.6.yaml
CHANGELOG.md
README.md README.md
extra-source-files: extra-source-files:
@@ -43,11 +46,6 @@ flag internal-downloader
default: False default: False
manual: True manual: True
flag no-exe
description: Don't build any executables
default: False
manual: True
library library
exposed-modules: exposed-modules:
GHCup GHCup
@@ -58,7 +56,6 @@ library
GHCup.Requirements GHCup.Requirements
GHCup.Types GHCup.Types
GHCup.Types.JSON GHCup.Types.JSON
GHCup.Types.JSON.Utils
GHCup.Types.Optics GHCup.Types.Optics
GHCup.Utils GHCup.Utils
GHCup.Utils.Dirs GHCup.Utils.Dirs
@@ -97,7 +94,7 @@ library
build-depends: build-depends:
, aeson >=1.4 , aeson >=1.4
, async >=0.8 && <2.3 , async >=0.8 && <2.3
, base >=4.12 && <5 , base >=4.13 && <5
, base16-bytestring >=0.1.1.6 && <1.1 , base16-bytestring >=0.1.1.6 && <1.1
, binary ^>=0.8.6.0 , binary ^>=0.8.6.0
, bytestring ^>=0.10 , bytestring ^>=0.10
@@ -111,10 +108,11 @@ library
, disk-free-space ^>=0.1.0.1 , disk-free-space ^>=0.1.0.1
, filepath ^>=1.4.2.1 , filepath ^>=1.4.2.1
, haskus-utils-types ^>=1.5 , haskus-utils-types ^>=1.5
, haskus-utils-variant ^>=3.2.1 , haskus-utils-variant >=3.0 && <3.2
, HsYAML-aeson ^>=0.2.0.0
, libarchive ^>=3.0.3.0 , libarchive ^>=3.0.3.0
, lzma-static ^>=5.2.5.3 , lzma-static ^>=5.2.5.3
, megaparsec >=8.0.0 && <9.3 , megaparsec >=8.0.0 && <9.1
, mtl ^>=2.2 , mtl ^>=2.2
, optics ^>=0.4 , optics ^>=0.4
, os-release ^>=1.0.0 , os-release ^>=1.0.0
@@ -122,11 +120,9 @@ library
, pretty-terminal ^>=0.1.0.0 , pretty-terminal ^>=0.1.0.0
, regex-posix ^>=0.96 , regex-posix ^>=0.96
, resourcet ^>=1.2.2 , resourcet ^>=1.2.2
, retry ^>=0.8.1.2
, safe ^>=0.3.18 , safe ^>=0.3.18
, safe-exceptions ^>=0.1 , safe-exceptions ^>=0.1
, split ^>=0.2.3.4 , split ^>=0.2.3.4
, streamly ^>=0.8.2
, strict-base ^>=0.4 , strict-base ^>=0.4
, template-haskell >=2.7 && <2.18 , template-haskell >=2.7 && <2.18
, temporary ^>=1.3 , temporary ^>=1.3
@@ -139,7 +135,6 @@ library
, vector ^>=0.12 , vector ^>=0.12
, versions >=4.0.1 && <5.1 , versions >=4.0.1 && <5.1
, word8 ^>=0.1.3 , word8 ^>=0.1.3
, yaml-streamly ^>=0.12.0
, zlib ^>=0.6.2.2 , zlib ^>=0.6.2.2
if (flag(internal-downloader) && !os(windows)) if (flag(internal-downloader) && !os(windows))
@@ -153,28 +148,21 @@ library
if os(windows) if os(windows)
cpp-options: -DIS_WINDOWS cpp-options: -DIS_WINDOWS
other-modules: other-modules: GHCup.Utils.File.Windows
GHCup.Utils.File.Windows
GHCup.Utils.Prelude.Windows
GHCup.Utils.Windows
build-depends: build-depends:
, bzlib , bzlib
, process ^>=1.6.11.0 , process ^>=1.6.11.0
, retry ^>=0.8.1.2
, Win32 ^>=2.10 , Win32 ^>=2.10
else else
other-modules: other-modules:
GHCup.Utils.File.Posix GHCup.Utils.File.Posix
GHCup.Utils.File.Posix.Foreign System.Console.Terminal.Common
GHCup.Utils.File.Posix.Traversals System.Console.Terminal.Posix
GHCup.Utils.Posix
GHCup.Utils.Prelude.Posix
c-sources: cbits/dirutils.c
build-depends: build-depends:
, bz2 >=0.5.0.5 && <1.1 , bz2 >=0.5.0.5 && <1.1
, terminal-size ^>=0.3.2.1
, unix ^>=2.7 , unix ^>=2.7
, unix-bytestring ^>=0.3.7.3 , unix-bytestring ^>=0.3.7.3
@@ -184,26 +172,23 @@ library
executable ghcup executable ghcup
main-is: Main.hs main-is: Main.hs
other-modules: other-modules: GHCup.OptParse.Install
GHCup.OptParse GHCup.OptParse.Common
GHCup.OptParse.ChangeLog GHCup.OptParse.Set
GHCup.OptParse.Common GHCup.OptParse.UnSet
GHCup.OptParse.Compile GHCup.OptParse.Rm
GHCup.OptParse.Config GHCup.OptParse.Compile
GHCup.OptParse.DInfo GHCup.OptParse.Config
GHCup.OptParse.GC GHCup.OptParse.Whereis
GHCup.OptParse.Install GHCup.OptParse.List
GHCup.OptParse.List GHCup.OptParse.DInfo
GHCup.OptParse.Nuke GHCup.OptParse.Upgrade
GHCup.OptParse.Prefetch GHCup.OptParse.ToolRequirements
GHCup.OptParse.Rm GHCup.OptParse.ChangeLog
GHCup.OptParse.Run GHCup.OptParse.Nuke
GHCup.OptParse.Set GHCup.OptParse.Prefetch
GHCup.OptParse.ToolRequirements GHCup.OptParse.GC
GHCup.OptParse.UnSet GHCup.OptParse
GHCup.OptParse.Upgrade
GHCup.OptParse.Whereis
hs-source-dirs: app/ghcup hs-source-dirs: app/ghcup
default-language: Haskell2010 default-language: Haskell2010
default-extensions: default-extensions:
@@ -224,35 +209,29 @@ executable ghcup
, aeson >=1.4 , aeson >=1.4
, aeson-pretty ^>=0.8.8 , aeson-pretty ^>=0.8.8
, async ^>=2.2.3 , async ^>=2.2.3
, base >=4.12 && <5 , base >=4.13 && <5
, bytestring ^>=0.10 , bytestring ^>=0.10
, cabal-plan ^>=0.7.2 , cabal-plan ^>=0.7.2
, containers ^>=0.6 , containers ^>=0.6
, deepseq ^>=1.4 , deepseq ^>=1.4
, directory ^>=1.3.6.0
, filepath ^>=1.4.2.1 , filepath ^>=1.4.2.1
, ghcup , ghcup
, haskus-utils-variant ^>=3.2.1 , haskus-utils-variant >=3.0 && <3.2
, HsYAML-aeson ^>=0.2.0.0
, libarchive ^>=3.0.3.0 , libarchive ^>=3.0.3.0
, megaparsec >=8.0.0 && <9.3 , megaparsec >=8.0.0 && <9.1
, mtl ^>=2.2 , mtl ^>=2.2
, optparse-applicative >=0.15.1.0 && <0.18 , optparse-applicative >=0.15.1.0 && <0.17
, pretty ^>=1.1.3.1 , pretty ^>=1.1.3.1
, pretty-terminal ^>=0.1.0.0 , pretty-terminal ^>=0.1.0.0
, process ^>=1.6.11.0
, resourcet ^>=1.2.2 , resourcet ^>=1.2.2
, safe ^>=0.3.18 , safe ^>=0.3.18
, safe-exceptions ^>=0.1 , safe-exceptions ^>=0.1
, tagsoup ^>=0.14
, template-haskell >=2.7 && <2.18 , template-haskell >=2.7 && <2.18
, temporary ^>=1.3
, text ^>=1.2.4.0 , text ^>=1.2.4.0
, unordered-containers ^>=0.2
, uri-bytestring ^>=0.3.2.2 , uri-bytestring ^>=0.3.2.2
, utf8-string ^>=1.0 , utf8-string ^>=1.0
, vector ^>=0.12
, versions >=4.0.1 && <5.1 , versions >=4.0.1 && <5.1
, yaml-streamly ^>=0.12.0
if flag(internal-downloader) if flag(internal-downloader)
cpp-options: -DINTERNAL_DOWNLOADER cpp-options: -DINTERNAL_DOWNLOADER
@@ -263,17 +242,56 @@ executable ghcup
build-depends: build-depends:
, brick ^>=0.64 , brick ^>=0.64
, transformers ^>=0.5 , transformers ^>=0.5
, unix ^>=2.7 , vector ^>=0.12
, vty >=5.28.2 && <5.34 , vty >=5.28.2 && <5.34
if os(windows) if os(windows)
cpp-options: -DIS_WINDOWS cpp-options: -DIS_WINDOWS
else executable ghcup-gen
build-depends: unix ^>=2.7 main-is: Main.hs
hs-source-dirs: app/ghcup-gen
other-modules: Validate
default-language: Haskell2010
default-extensions:
DeriveGeneric
LambdaCase
MultiWayIf
NamedFieldPuns
PackageImports
QuasiQuotes
RecordWildCards
ScopedTypeVariables
StrictData
TupleSections
TypeApplications
TypeFamilies
ViewPatterns
if flag(no-exe) ghc-options:
buildable: False -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns
-fwarn-incomplete-record-updates -threaded
build-depends:
, base >=4.13 && <5
, bytestring ^>=0.10
, containers ^>=0.6
, filepath ^>=1.4.2.1
, ghcup
, haskus-utils-variant >=3.0 && <3.2
, HsYAML-aeson ^>=0.2.0.0
, libarchive ^>=3.0.3.0
, mtl ^>=2.2
, optics ^>=0.4
, optparse-applicative >=0.15.1.0 && <0.17
, pretty ^>=1.1.3.1
, pretty-terminal ^>=0.1.0.0
, regex-posix ^>=0.96
, resourcet ^>=1.2.2
, safe-exceptions ^>=0.1
, text ^>=1.2.4.0
, transformers ^>=0.5
, versions >=4.0.1 && <5.1
test-suite ghcup-test test-suite ghcup-test
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
@@ -283,7 +301,6 @@ test-suite ghcup-test
other-modules: other-modules:
GHCup.ArbitraryTypes GHCup.ArbitraryTypes
GHCup.Types.JSONSpec GHCup.Types.JSONSpec
GHCup.Utils.FileSpec
Spec Spec
default-language: Haskell2010 default-language: Haskell2010
@@ -300,18 +317,15 @@ test-suite ghcup-test
-fwarn-incomplete-record-updates -fwarn-incomplete-record-updates
build-depends: build-depends:
, base >=4.12 && <5 , base >=4.13 && <5
, bytestring ^>=0.10 , bytestring ^>=0.10
, containers ^>=0.6 , containers ^>=0.6
, directory ^>=1.3.6.0 , generic-arbitrary ^>=0.1.0
, filepath ^>=1.4.2.1
, generic-arbitrary >=0.1.0 && <0.3
, ghcup , ghcup
, hspec >=2.7.10 && <2.10 , hspec ^>=2.7.10
, hspec-golden-aeson ^>=0.9 , hspec-golden-aeson ^>=0.9
, QuickCheck ^>=2.14.1 , QuickCheck ^>=2.14.1
, quickcheck-arbitrary-adt ^>=0.3.1.0 , quickcheck-arbitrary-adt ^>=0.3.1.0
, streamly ^>=0.8.2
, text ^>=1.2.4.0 , text ^>=1.2.4.0
, uri-bytestring ^>=0.3.2.2 , uri-bytestring ^>=0.3.2.2
, versions >=4.0.1 && <5.1 , versions >=4.0.1 && <5.1

View File

@@ -4,5 +4,7 @@ cradle:
path: ./lib path: ./lib
- component: "ghcup:exe:ghcup" - component: "ghcup:exe:ghcup"
path: ./app/ghcup path: ./app/ghcup
- component: "ghcup:exe:ghcup-gen"
path: "./app/ghcup-gen"
- component: "ghcup:test:ghcup-test" - component: "ghcup:test:ghcup-test"
path: ./test path: ./test

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,7 @@ import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
hiding ( throwM ) hiding ( throwM )
import Data.Aeson import Data.Aeson
import Data.Bifunctor
import Data.ByteString ( ByteString ) import Data.ByteString ( ByteString )
#if defined(INTERNAL_DOWNLOADER) #if defined(INTERNAL_DOWNLOADER)
import Data.CaseInsensitive ( mk ) import Data.CaseInsensitive ( mk )
@@ -69,6 +70,7 @@ import Prelude hiding ( abs
, writeFile , writeFile
) )
import Safe import Safe
import System.Directory
import System.Environment import System.Environment
import System.Exit import System.Exit
import System.FilePath import System.FilePath
@@ -85,7 +87,7 @@ import qualified Data.Map.Strict as M
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import qualified Data.Yaml.Aeson as Y import qualified Data.YAML.Aeson as Y
@@ -120,31 +122,34 @@ getDownloadsF = do
Settings { urlSource } <- lift getSettings Settings { urlSource } <- lift getSettings
case urlSource of case urlSource of
GHCupURL -> liftE $ getBase ghcupURL GHCupURL -> liftE $ getBase ghcupURL
(OwnSource exts) -> do (OwnSource url) -> liftE $ getBase url
ext <- liftE $ mapM (either pure getBase) exts
mergeGhcupInfo ext
(OwnSpec av) -> pure av (OwnSpec av) -> pure av
(AddSource exts) -> do (AddSource (Left ext)) -> do
base <- liftE $ getBase ghcupURL base <- liftE $ getBase ghcupURL
ext <- liftE $ mapM (either pure getBase) exts pure (mergeGhcupInfo base ext)
mergeGhcupInfo (base:ext) (AddSource (Right uri)) -> do
base <- liftE $ getBase ghcupURL
ext <- liftE $ getBase uri
pure (mergeGhcupInfo base ext)
where where
mergeGhcupInfo :: MonadFail m
=> [GHCupInfo] mergeGhcupInfo :: GHCupInfo -- ^ base to merge with
-> m GHCupInfo -> GHCupInfo -- ^ extension overwriting the base
mergeGhcupInfo [] = fail "mergeGhcupInfo: internal error: need at least one GHCupInfo" -> GHCupInfo
mergeGhcupInfo xs@(GHCupInfo{}: _) = mergeGhcupInfo (GHCupInfo tr base base2) (GHCupInfo _ ext ext2) =
let newDownloads = M.unionsWith (M.unionWith (\_ b2 -> b2)) (_ghcupDownloads <$> xs) let newDownloads = M.mapWithKey (\k a -> case M.lookup k ext of
newGlobalTools = M.unionsWith (\_ a2 -> a2 ) (_globalTools <$> xs) Just a' -> M.union a' a
newToolReqs = M.unionsWith (M.unionWith (\_ b2 -> b2)) (_toolRequirements <$> xs) Nothing -> a
in pure $ GHCupInfo newToolReqs newDownloads newGlobalTools ) base
newGlobalTools = M.union base2 ext2
in GHCupInfo tr newDownloads newGlobalTools
yamlFromCache :: (MonadReader env m, HasDirs env) => URI -> m FilePath yamlFromCache :: (MonadReader env m, HasDirs env) => URI -> m FilePath
yamlFromCache uri = do yamlFromCache uri = do
Dirs{..} <- getDirs Dirs{..} <- getDirs
pure (fromGHCupPath cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri)) pure (cacheDir </> (T.unpack . decUTF8Safe . urlBaseName . view pathL' $ uri))
etagsFile :: FilePath -> FilePath etagsFile :: FilePath -> FilePath
@@ -178,14 +183,15 @@ getBase uri = do
-- if we didn't get a filepath from the download, use the cached yaml -- if we didn't get a filepath from the download, use the cached yaml
actualYaml <- maybe (lift $ yamlFromCache uri) pure mYaml actualYaml <- maybe (lift $ yamlFromCache uri) pure mYaml
yamlContents <- liftIOException doesNotExistErrorType (FileDoesNotExistError actualYaml) $ liftIO $ L.readFile actualYaml
lift $ logDebug $ "Decoding yaml at: " <> T.pack actualYaml lift $ logDebug $ "Decoding yaml at: " <> T.pack actualYaml
liftE liftE
. onE_ (onError actualYaml) . onE_ (onError actualYaml)
. lEM' @_ @_ @'[JSONError] (\(displayException -> e) -> JSONDecodeError $ unlines [e, "Consider removing " <> actualYaml <> " manually."]) . lE' @_ @_ @'[JSONError] JSONDecodeError
. liftIO . first (\(_, e) -> unlines [e, "Consider removing " <> actualYaml <> " manually."])
. Y.decodeFileEither . Y.decode1
$ actualYaml $ yamlContents
where where
-- On error, remove the etags file and set access time to 0. This should ensure the next invocation -- On error, remove the etags file and set access time to 0. This should ensure the next invocation
-- may re-download and succeed. -- may re-download and succeed.
@@ -238,18 +244,14 @@ getBase uri = do
e <- liftIO $ doesFileExist json_file e <- liftIO $ doesFileExist json_file
currentTime <- liftIO getCurrentTime currentTime <- liftIO getCurrentTime
Dirs { cacheDir } <- lift getDirs Dirs { cacheDir } <- lift getDirs
Settings { metaCache } <- lift getSettings
-- for local files, let's short-circuit and ignore access time -- for local files, let's short-circuit and ignore access time
if | scheme == "file" -> liftE $ download uri' Nothing Nothing (fromGHCupPath cacheDir) Nothing True if | scheme == "file" -> liftE $ download uri' Nothing Nothing cacheDir Nothing True
| e -> do | e -> do
accessTime <- fmap utcTimeToPOSIXSeconds $ liftIO $ getAccessTime json_file accessTime <- liftIO $ getAccessTime json_file
let sinceLastAccess = utcTimeToPOSIXSeconds currentTime - accessTime
let cacheInterval = fromInteger metaCache
lift $ logDebug $ "last access was " <> T.pack (show sinceLastAccess) <> " ago, cache interval is " <> T.pack (show cacheInterval)
-- access time won't work on most linuxes, but we can try regardless -- access time won't work on most linuxes, but we can try regardless
if | metaCache <= 0 -> dlWithMod currentTime json_file if | ((utcTimeToPOSIXSeconds currentTime - utcTimeToPOSIXSeconds accessTime) > 300) ->
| (sinceLastAccess > cacheInterval) ->
-- no access in last 5 minutes, re-check upstream mod time -- no access in last 5 minutes, re-check upstream mod time
dlWithMod currentTime json_file dlWithMod currentTime json_file
| otherwise -> pure json_file | otherwise -> pure json_file
@@ -580,7 +582,7 @@ downloadCached dli mfn = do
True -> downloadCached' dli mfn Nothing True -> downloadCached' dli mfn Nothing
False -> do False -> do
tmp <- lift withGHCupTmpDir tmp <- lift withGHCupTmpDir
liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) (fromGHCupPath tmp) mfn False liftE $ download (_dlUri dli) Nothing (Just (_dlHash dli)) tmp mfn False
downloadCached' :: ( MonadReader env m downloadCached' :: ( MonadReader env m
@@ -598,7 +600,7 @@ downloadCached' :: ( MonadReader env m
-> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath -> Excepts '[DigestError , DownloadFailed, GPGError] m FilePath
downloadCached' dli mfn mDestDir = do downloadCached' dli mfn mDestDir = do
Dirs { cacheDir } <- lift getDirs Dirs { cacheDir } <- lift getDirs
let destDir = fromMaybe (fromGHCupPath cacheDir) mDestDir let destDir = fromMaybe cacheDir mDestDir
let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) mfn let fn = fromMaybe ((T.unpack . decUTF8Safe) $ urlBaseName $ view (dlUri % pathL') dli) mfn
let cachfile = destDir </> fn let cachfile = destDir </> fn
fileExists <- liftIO $ doesFileExist cachfile fileExists <- liftIO $ doesFileExist cachfile

View File

@@ -27,7 +27,6 @@ import Data.CaseInsensitive ( CI )
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Versions import Data.Versions
import Haskus.Utils.Variant import Haskus.Utils.Variant
import System.FilePath
import Text.PrettyPrint hiding ( (<>) ) import Text.PrettyPrint hiding ( (<>) )
import Text.PrettyPrint.HughesPJClass hiding ( (<>) ) import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
import URI.ByteString import URI.ByteString
@@ -146,13 +145,6 @@ instance Pretty NotInstalled where
pPrint (NotInstalled tool ver) = pPrint (NotInstalled tool ver) =
text "The version" <+> pPrint ver <+> text "of the tool" <+> pPrint tool <+> text "is not installed." text "The version" <+> pPrint ver <+> text "of the tool" <+> pPrint tool <+> text "is not installed."
data UninstallFailed = UninstallFailed FilePath [FilePath]
deriving Show
instance Pretty UninstallFailed where
pPrint (UninstallFailed dir files) =
text "The following files survived uninstallation: " <+> pPrint files <+> text "...consider removing" <+> pPrint dir <+> text "manually."
-- | An executable was expected to be in PATH, but was not found. -- | An executable was expected to be in PATH, but was not found.
data NotFoundInPATH = NotFoundInPATH FilePath data NotFoundInPATH = NotFoundInPATH FilePath
deriving Show deriving Show
@@ -299,24 +291,6 @@ instance Pretty HadrianNotFound where
pPrint HadrianNotFound = pPrint HadrianNotFound =
text "Could not find Hadrian build files. Does this GHC version support Hadrian builds?" text "Could not find Hadrian build files. Does this GHC version support Hadrian builds?"
data GHCupShadowed = GHCupShadowed
FilePath -- shadow binary
FilePath -- upgraded binary
Version -- upgraded version
deriving Show
instance Pretty GHCupShadowed where
pPrint (GHCupShadowed sh up _) =
text ("ghcup is shadowed by "
<> sh
<> ". The upgrade will not be in effect, unless you remove "
<> sh
<> " or make sure "
<> takeDirectory up
<> " comes before "
<> takeDirectory sh
<> " in PATH."
)
------------------------- -------------------------
--[ High-level errors ]-- --[ High-level errors ]--

View File

@@ -1,10 +1,9 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TemplateHaskell #-}
{-| {-|
@@ -23,15 +22,11 @@ import GHCup.Errors
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
import GHCup.Types.JSON ( ) import GHCup.Types.JSON ( )
import GHCup.Utils.Dirs
import GHCup.Utils.File import GHCup.Utils.File
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import GHCup.Utils.String.QQ import GHCup.Utils.String.QQ
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
import Control.Applicative import Control.Applicative
import Control.Exception.Safe import Control.Exception.Safe
import Control.Monad import Control.Monad
@@ -47,6 +42,7 @@ import Prelude hiding ( abs
, writeFile , writeFile
) )
import System.Info import System.Info
import System.Directory
import System.OsRelease import System.OsRelease
import Text.PrettyPrint.HughesPJClass ( prettyShow ) import Text.PrettyPrint.HughesPJClass ( prettyShow )
import Text.Regex.Posix import Text.Regex.Posix

View File

@@ -67,9 +67,3 @@ prettyRequirements Requirements {..} =
else "" else ""
n = if not . T.null $ _notes then "\n Note: " <> _notes else "" n = if not . T.null $ _notes then "\n Note: " <> _notes else ""
in "System requirements " <> d <> n in "System requirements " <> d <> n
rawRequirements :: Requirements -> T.Text
rawRequirements Requirements {..} =
if not . null $ _distroPKGs
then T.intercalate " " _distroPKGs
else ""

View File

@@ -6,7 +6,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-| {-|
@@ -26,23 +25,17 @@ module GHCup.Types
) )
where where
import {-# SOURCE #-} GHCup.Utils.Dirs ( fromGHCupPath )
import {-# SOURCE #-} GHCup.Utils.Dirs ( GHCupPath )
import Control.DeepSeq ( NFData, rnf ) import Control.DeepSeq ( NFData, rnf )
import Data.Map.Strict ( Map ) import Data.Map.Strict ( Map )
import Data.List.NonEmpty ( NonEmpty (..) ) import Data.List.NonEmpty ( NonEmpty (..) )
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Versions import Data.Versions
import GHC.IO.Exception ( ExitCode ) import Text.PrettyPrint.HughesPJClass (Pretty, pPrint, text)
import Optics ( makeLenses )
import Text.PrettyPrint.HughesPJClass (Pretty, pPrint, text, (<+>))
import URI.ByteString import URI.ByteString
#if defined(BRICK) #if defined(BRICK)
import Graphics.Vty ( Key(..) ) import Graphics.Vty ( Key(..) )
#endif #endif
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T import qualified Data.Text as T
import qualified GHC.Generics as GHC import qualified GHC.Generics as GHC
@@ -289,9 +282,9 @@ instance Pretty TarDir where
-- | Where to fetch GHCupDownloads from. -- | Where to fetch GHCupDownloads from.
data URLSource = GHCupURL data URLSource = GHCupURL
| OwnSource [Either GHCupInfo URI] -- ^ complete source list | OwnSource URI
| OwnSpec GHCupInfo | OwnSpec GHCupInfo
| AddSource [Either GHCupInfo URI] -- ^ merge with GHCupURL | AddSource (Either GHCupInfo URI) -- ^ merge with GHCupURL
deriving (GHC.Generic, Show) deriving (GHC.Generic, Show)
instance NFData URLSource instance NFData URLSource
@@ -301,7 +294,6 @@ instance NFData (URIRef Absolute) where
data UserSettings = UserSettings data UserSettings = UserSettings
{ uCache :: Maybe Bool { uCache :: Maybe Bool
, uMetaCache :: Maybe Integer
, uNoVerify :: Maybe Bool , uNoVerify :: Maybe Bool
, uVerbose :: Maybe Bool , uVerbose :: Maybe Bool
, uKeepDirs :: Maybe KeepDirs , uKeepDirs :: Maybe KeepDirs
@@ -314,13 +306,12 @@ data UserSettings = UserSettings
deriving (Show, GHC.Generic) deriving (Show, GHC.Generic)
defaultUserSettings :: UserSettings defaultUserSettings :: UserSettings
defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing defaultUserSettings = UserSettings Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
fromSettings :: Settings -> Maybe KeyBindings -> UserSettings fromSettings :: Settings -> Maybe KeyBindings -> UserSettings
fromSettings Settings{..} Nothing = fromSettings Settings{..} Nothing =
UserSettings { UserSettings {
uCache = Just cache uCache = Just cache
, uMetaCache = Just metaCache
, uNoVerify = Just noVerify , uNoVerify = Just noVerify
, uVerbose = Just verbose , uVerbose = Just verbose
, uKeepDirs = Just keepDirs , uKeepDirs = Just keepDirs
@@ -344,7 +335,6 @@ fromSettings Settings{..} (Just KeyBindings{..}) =
} }
in UserSettings { in UserSettings {
uCache = Just cache uCache = Just cache
, uMetaCache = Just metaCache
, uNoVerify = Just noVerify , uNoVerify = Just noVerify
, uVerbose = Just verbose , uVerbose = Just verbose
, uKeepDirs = Just keepDirs , uKeepDirs = Just keepDirs
@@ -420,7 +410,6 @@ instance NFData LeanAppState
data Settings = Settings data Settings = Settings
{ cache :: Bool { cache :: Bool
, metaCache :: Integer
, noVerify :: Bool , noVerify :: Bool
, keepDirs :: KeepDirs , keepDirs :: KeepDirs
, downloader :: Downloader , downloader :: Downloader
@@ -432,22 +421,15 @@ data Settings = Settings
} }
deriving (Show, GHC.Generic) deriving (Show, GHC.Generic)
defaultMetaCache :: Integer
defaultMetaCache = 300 -- 5 minutes
defaultSettings :: Settings
defaultSettings = Settings False defaultMetaCache False Never Curl False GHCupURL False GPGNone False
instance NFData Settings instance NFData Settings
data Dirs = Dirs data Dirs = Dirs
{ baseDir :: GHCupPath { baseDir :: FilePath
, binDir :: FilePath , binDir :: FilePath
, cacheDir :: GHCupPath , cacheDir :: FilePath
, logsDir :: GHCupPath , logsDir :: FilePath
, confDir :: GHCupPath , confDir :: FilePath
, dbDir :: GHCupPath , recycleDir :: FilePath -- mainly used on windows
, recycleDir :: GHCupPath -- mainly used on windows
} }
deriving (Show, GHC.Generic) deriving (Show, GHC.Generic)
@@ -492,10 +474,6 @@ data SetGHC = SetGHCOnly -- ^ unversioned 'ghc'
| SetGHC_XYZ -- ^ ghc-x.y.z | SetGHC_XYZ -- ^ ghc-x.y.z
deriving (Eq, Show) deriving (Eq, Show)
data SetHLS = SetHLSOnly -- ^ unversioned 'hls'
| SetHLS_XYZ -- ^ haskell-language-server-a.b.c~x.y.z, where a.b.c is GHC version and x.y.z is HLS version
deriving (Eq, Show)
data PlatformResult = PlatformResult data PlatformResult = PlatformResult
{ _platform :: Platform { _platform :: Platform
@@ -608,42 +586,3 @@ data LoggerConfig = LoggerConfig
instance NFData LoggerConfig where instance NFData LoggerConfig where
rnf (LoggerConfig !lcPrintDebug !_ !_ !fancyColors) = rnf (lcPrintDebug, fancyColors) rnf (LoggerConfig !lcPrintDebug !_ !_ !fancyColors) = rnf (lcPrintDebug, fancyColors)
data ProcessError = NonZeroExit Int FilePath [String]
| PTerminated FilePath [String]
| PStopped FilePath [String]
| NoSuchPid FilePath [String]
deriving Show
instance Pretty ProcessError where
pPrint (NonZeroExit e exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "failed with exit code" <+> text (show e <> ".")
pPrint (PTerminated exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "terminated."
pPrint (PStopped exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "stopped."
pPrint (NoSuchPid exe args) =
text "Could not find PID for process running " <+> pPrint exe <+> text " with arguments " <+> text (show args) <+> text "."
data CapturedProcess = CapturedProcess
{ _exitCode :: ExitCode
, _stdOut :: BL.ByteString
, _stdErr :: BL.ByteString
}
deriving (Eq, Show)
makeLenses ''CapturedProcess
data InstallDir = IsolateDir FilePath
| GHCupInternal
deriving (Eq, Show)
data InstallDirResolved = IsolateDirResolved FilePath
| GHCupDir GHCupPath
| GHCupBinDir FilePath
deriving (Eq, Show)
fromInstallDir :: InstallDirResolved -> FilePath
fromInstallDir (IsolateDirResolved fp) = fp
fromInstallDir (GHCupDir fp) = fromGHCupPath fp
fromInstallDir (GHCupBinDir fp) = fp

View File

@@ -22,8 +22,10 @@ Portability : portable
module GHCup.Types.JSON where module GHCup.Types.JSON where
import GHCup.Types import GHCup.Types
import GHCup.Types.JSON.Utils
import GHCup.Utils.MegaParsec import GHCup.Utils.MegaParsec
import GHCup.Utils.Prelude
import GHCup.Utils.Logger () -- TH is broken shite and needs GHCup.Utils.Logger for linking, although we don't depend on the file.
-- This is due to the boot file.
import Control.Applicative ( (<|>) ) import Control.Applicative ( (<|>) )
import Data.Aeson hiding (Key) import Data.Aeson hiding (Key)
@@ -38,7 +40,6 @@ import Text.Casing
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding.Error as E
import qualified Text.Megaparsec as MP import qualified Text.Megaparsec as MP
import qualified Text.Megaparsec.Char as MPC import qualified Text.Megaparsec.Char as MPC
@@ -77,39 +78,7 @@ instance FromJSON Tag where
x -> pure (UnknownTag x) x -> pure (UnknownTag x)
instance ToJSON URI where instance ToJSON URI where
toJSON = toJSON . E.decodeUtf8With E.lenientDecode . serializeURIRef' toJSON = toJSON . decUTF8Safe . serializeURIRef'
instance FromJSON URLSource where
parseJSON v =
parseGHCupURL v
<|> parseOwnSourceLegacy v
<|> parseOwnSourceNew1 v
<|> parseOwnSourceNew2 v
<|> parseOwnSpec v
<|> legacyParseAddSource v
<|> newParseAddSource v
where
parseOwnSourceLegacy = withObject "URLSource" $ \o -> do
r :: URI <- o .: "OwnSource"
pure (OwnSource [Right r])
parseOwnSourceNew1 = withObject "URLSource" $ \o -> do
r :: [URI] <- o .: "OwnSource"
pure (OwnSource (fmap Right r))
parseOwnSourceNew2 = withObject "URLSource" $ \o -> do
r :: [Either GHCupInfo URI] <- o .: "OwnSource"
pure (OwnSource r)
parseOwnSpec = withObject "URLSource" $ \o -> do
r :: GHCupInfo <- o .: "OwnSpec"
pure (OwnSpec r)
parseGHCupURL = withObject "URLSource" $ \o -> do
_ :: [Value] <- o .: "GHCupURL"
pure GHCupURL
legacyParseAddSource = withObject "URLSource" $ \o -> do
r :: Either GHCupInfo URI <- o .: "AddSource"
pure (AddSource [r])
newParseAddSource = withObject "URLSource" $ \o -> do
r :: [Either GHCupInfo URI] <- o .: "AddSource"
pure (AddSource r)
instance FromJSON URI where instance FromJSON URI where
parseJSON = withText "URL" $ \t -> parseJSON = withText "URL" $ \t ->
@@ -346,7 +315,7 @@ deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''Requir
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''DownloadInfo
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''VersionInfo
deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupInfo deriveJSON defaultOptions { fieldLabelModifier = removeLensFieldLabel } ''GHCupInfo
deriveToJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''URLSource deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''URLSource
deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''Key deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField } ''Key
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "k-") . T.pack . kebab $ str' } ''UserKeyBindings deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "k-") . T.pack . kebab $ str' } ''UserKeyBindings
deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "u-") . T.pack . kebab $ str' } ''UserSettings deriveJSON defaultOptions { fieldLabelModifier = \str' -> maybe str' T.unpack . T.stripPrefix (T.pack "u-") . T.pack . kebab $ str' } ''UserSettings

View File

@@ -1,17 +0,0 @@
{-|
Module : GHCup.Types.JSON.Utils
Description : Utils for TH splices
Copyright : (c) Julian Ospald, 2020
License : LGPL-3.0
Maintainer : hasufell@hasufell.de
Stability : experimental
Portability : portable
-}
module GHCup.Types.JSON.Utils where
import qualified Data.Text as T
removeLensFieldLabel :: String -> String
removeLensFieldLabel str' =
maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'

View File

@@ -1,9 +1,9 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
@@ -22,21 +22,13 @@ installation and introspection of files/versions etc.
module GHCup.Utils module GHCup.Utils
( module GHCup.Utils.Dirs ( module GHCup.Utils.Dirs
, module GHCup.Utils , module GHCup.Utils
#if defined(IS_WINDOWS)
, module GHCup.Utils.Windows
#else
, module GHCup.Utils.Posix
#endif
) )
where where
#if defined(IS_WINDOWS) #if defined(IS_WINDOWS)
import GHCup.Utils.Windows
#else
import GHCup.Utils.Posix
#endif
import GHCup.Download import GHCup.Download
#endif
import GHCup.Errors import GHCup.Errors
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
@@ -59,7 +51,9 @@ import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
hiding ( throwM ) hiding ( throwM )
import Control.Monad.IO.Unlift ( MonadUnliftIO( withRunInIO ) ) import Control.Monad.IO.Unlift ( MonadUnliftIO( withRunInIO ) )
import Data.Bifunctor ( first ) #if defined(IS_WINDOWS)
import Data.Bits
#endif
import Data.ByteString ( ByteString ) import Data.ByteString ( ByteString )
import Data.Either import Data.Either
import Data.Foldable import Data.Foldable
@@ -67,13 +61,19 @@ import Data.List
import Data.List.NonEmpty ( NonEmpty( (:|) )) import Data.List.NonEmpty ( NonEmpty( (:|) ))
import Data.Maybe import Data.Maybe
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Versions hiding ( patch ) import Data.Versions
import GHC.IO.Exception import GHC.IO.Exception
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Optics import Optics
import Safe import Safe
import System.Directory hiding ( findFiles )
import System.FilePath import System.FilePath
import System.IO.Error import System.IO.Error
#if defined(IS_WINDOWS)
import System.Win32.Console
import System.Win32.File hiding ( copyFile )
import System.Win32.Types
#endif
import Text.Regex.Posix import Text.Regex.Posix
import URI.ByteString import URI.ByteString
@@ -86,9 +86,6 @@ import qualified Data.Text as T
import qualified Data.Text.Encoding as E import qualified Data.Text.Encoding as E
import qualified Text.Megaparsec as MP import qualified Text.Megaparsec as MP
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Streamly.Prelude as S
import Control.DeepSeq (force)
import GHC.IO (evaluate)
-- $setup -- $setup
@@ -113,8 +110,8 @@ import GHC.IO (evaluate)
-- >>> import Text.PrettyPrint.HughesPJClass ( prettyShow ) -- >>> import Text.PrettyPrint.HughesPJClass ( prettyShow )
-- >>> let lc = LoggerConfig { lcPrintDebug = False, consoleOutter = mempty, fileOutter = mempty, fancyColors = False } -- >>> let lc = LoggerConfig { lcPrintDebug = False, consoleOutter = mempty, fileOutter = mempty, fancyColors = False }
-- >>> dirs' <- getAllDirs -- >>> dirs' <- getAllDirs
-- >>> let installedVersions = [ ([pver|8.10.7|], "-debug+lol", Nothing), ([pver|8.10.4|], "", Nothing), ([pver|8.8.4|], "", Nothing), ([pver|8.8.3|], "", Nothing) ] -- >>> let installedVersions = [ ([pver|8.10.7|], Nothing), ([pver|8.10.4|], Nothing), ([pver|8.8.4|], Nothing), ([pver|8.8.3|], Nothing) ]
-- >>> let settings = Settings True 0 False Never Curl False GHCupURL True GPGNone False -- >>> let settings = Settings True False Never Curl False GHCupURL True GPGNone False
-- >>> let leanAppState = LeanAppState settings dirs' defaultKeyBindings lc -- >>> let leanAppState = LeanAppState settings dirs' defaultKeyBindings lc
-- >>> cwd <- getCurrentDirectory -- >>> cwd <- getCurrentDirectory
-- >>> (Right ref) <- pure $ parseURI strictURIParserOptions $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ ghcupURL) -- >>> (Right ref) <- pure $ parseURI strictURIParserOptions $ "file://" <> E.encodeUtf8 (T.pack cwd) <> "/data/metadata/" <> (urlBaseName . view pathL' $ ghcupURL)
@@ -127,32 +124,31 @@ import GHC.IO (evaluate)
------------------------ ------------------------
-- | Create a relative symlink destination for the binary directory, -- | The symlink destination of a ghc tool.
-- given a target toolpath. ghcLinkDestination :: ( MonadReader env m
binarySymLinkDestination :: ( MonadThrow m , HasDirs env
, MonadIO m , MonadThrow m, MonadIO m)
) => FilePath -- ^ the tool, such as 'ghc', 'haddock' etc.
=> FilePath -- ^ binary dir -> GHCTargetVersion
-> FilePath -- ^ the full toolpath -> m FilePath
-> m FilePath ghcLinkDestination tool ver = do
binarySymLinkDestination binDir toolPath = do Dirs {..} <- getDirs
toolPath' <- liftIO $ canonicalizePath toolPath ghcd <- ghcupGHCDir ver
binDir' <- liftIO $ canonicalizePath binDir pure (relativeSymlink binDir (ghcd </> "bin" </> tool))
pure (relativeSymlink binDir' toolPath')
-- | Removes the minor GHC symlinks, e.g. ghc-8.6.5. -- | Removes the minor GHC symlinks, e.g. ghc-8.6.5.
rmMinorGHCSymlinks :: ( MonadReader env m rmMinorSymlinks :: ( MonadReader env m
, HasDirs env , HasDirs env
, MonadIO m , MonadIO m
, HasLog env , HasLog env
, MonadThrow m , MonadThrow m
, MonadFail m , MonadFail m
, MonadMask m , MonadMask m
) )
=> GHCTargetVersion => GHCTargetVersion
-> Excepts '[NotInstalled] m () -> Excepts '[NotInstalled] m ()
rmMinorGHCSymlinks tv@GHCTargetVersion{..} = do rmMinorSymlinks tv@GHCTargetVersion{..} = do
Dirs {..} <- lift getDirs Dirs {..} <- lift getDirs
files <- liftE $ ghcToolFiles tv files <- liftE $ ghcToolFiles tv
@@ -164,17 +160,17 @@ rmMinorGHCSymlinks tv@GHCTargetVersion{..} = do
-- | Removes the set ghc version for the given target, if any. -- | Removes the set ghc version for the given target, if any.
rmPlainGHC :: ( MonadReader env m rmPlain :: ( MonadReader env m
, HasDirs env , HasDirs env
, HasLog env , HasLog env
, MonadThrow m , MonadThrow m
, MonadFail m , MonadFail m
, MonadIO m , MonadIO m
, MonadMask m , MonadMask m
) )
=> Maybe Text -- ^ target => Maybe Text -- ^ target
-> Excepts '[NotInstalled] m () -> Excepts '[NotInstalled] m ()
rmPlainGHC target = do rmPlain target = do
Dirs {..} <- lift getDirs Dirs {..} <- lift getDirs
mtv <- lift $ ghcSet target mtv <- lift $ ghcSet target
forM_ mtv $ \tv -> do forM_ mtv $ \tv -> do
@@ -190,17 +186,17 @@ rmPlainGHC target = do
-- | Remove the major GHC symlink, e.g. ghc-8.6. -- | Remove the major GHC symlink, e.g. ghc-8.6.
rmMajorGHCSymlinks :: ( MonadReader env m rmMajorSymlinks :: ( MonadReader env m
, HasDirs env , HasDirs env
, MonadIO m , MonadIO m
, HasLog env , HasLog env
, MonadThrow m , MonadThrow m
, MonadFail m , MonadFail m
, MonadMask m , MonadMask m
) )
=> GHCTargetVersion => GHCTargetVersion
-> Excepts '[NotInstalled] m () -> Excepts '[NotInstalled] m ()
rmMajorGHCSymlinks tv@GHCTargetVersion{..} = do rmMajorSymlinks tv@GHCTargetVersion{..} = do
Dirs {..} <- lift getDirs Dirs {..} <- lift getDirs
(mj, mi) <- getMajorMinorV _tvVersion (mj, mi) <- getMajorMinorV _tvVersion
let v' = intToText mj <> "." <> intToText mi let v' = intToText mj <> "." <> intToText mi
@@ -213,62 +209,6 @@ rmMajorGHCSymlinks tv@GHCTargetVersion{..} = do
lift $ hideError doesNotExistErrorType $ rmLink fullF lift $ hideError doesNotExistErrorType $ rmLink fullF
-- | Removes the minor HLS files, e.g. 'haskell-language-server-8.10.7~1.6.1.0'
-- and 'haskell-language-server-wrapper-1.6.1.0'.
rmMinorHLSSymlinks :: ( MonadReader env m
, HasDirs env
, MonadIO m
, HasLog env
, MonadThrow m
, MonadFail m
, MonadMask m
)
=> Version
-> Excepts '[NotInstalled] m ()
rmMinorHLSSymlinks ver = do
Dirs {..} <- lift getDirs
hlsBins <- hlsAllBinaries ver
forM_ hlsBins $ \f -> do
let fullF = binDir </> f
lift $ logDebug ("rm -f " <> T.pack fullF)
-- on unix, this may be either a file (legacy) or a symlink
-- on windows, this is always a file... hence 'rmFile'
-- works consistently across platforms
lift $ rmFile fullF
-- | Removes the set HLS version, if any.
rmPlainHLS :: ( MonadReader env m
, HasDirs env
, HasLog env
, MonadThrow m
, MonadFail m
, MonadIO m
, MonadMask m
)
=> Excepts '[NotInstalled] m ()
rmPlainHLS = do
Dirs {..} <- lift getDirs
-- delete 'haskell-language-server-8.10.7'
hlsBins <- fmap (filter (\f -> not ("haskell-language-server-wrapper" `isPrefixOf` f) && ('~' `notElem` f)))
$ liftIO $ handleIO (\_ -> pure []) $ findFiles
binDir
(makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString))
forM_ hlsBins $ \f -> do
let fullF = binDir </> f
lift $ logDebug ("rm -f " <> T.pack fullF)
if isWindows
then lift $ rmLink fullF
else lift $ rmFile fullF
-- 'haskell-language-server-wrapper'
let hlswrapper = binDir </> "haskell-language-server-wrapper" <> exeExt
lift $ logDebug ("rm -f " <> T.pack hlswrapper)
if isWindows
then lift $ hideError doesNotExistErrorType $ rmLink hlswrapper
else lift $ hideError doesNotExistErrorType $ rmFile hlswrapper
----------------------------------- -----------------------------------
@@ -280,14 +220,14 @@ rmPlainHLS = do
ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool
ghcInstalled ver = do ghcInstalled ver = do
ghcdir <- ghcupGHCDir ver ghcdir <- ghcupGHCDir ver
liftIO $ doesDirectoryExist (fromGHCupPath ghcdir) liftIO $ doesDirectoryExist ghcdir
-- | Whether the given GHC version is installed from source. -- | Whether the given GHC version is installed from source.
ghcSrcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool ghcSrcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool
ghcSrcInstalled ver = do ghcSrcInstalled ver = do
ghcdir <- ghcupGHCDir ver ghcdir <- ghcupGHCDir ver
liftIO $ doesFileExist (fromGHCupPath ghcdir </> ghcUpSrcBuiltFile) liftIO $ doesFileExist (ghcdir </> ghcUpSrcBuiltFile)
-- | Whether the given GHC version is set as the current. -- | Whether the given GHC version is set as the current.
@@ -320,17 +260,17 @@ ghcSet mtarget = do
MP.setInput rest MP.setInput rest
pure x pure x
) )
<* MP.some pathSep <* pathSep
<* MP.takeRest <* MP.takeRest
<* MP.eof <* MP.eof
ghcSubPath = MP.some pathSep <* MP.chunk "ghc" *> MP.some pathSep ghcSubPath = pathSep <* MP.chunk "ghc" *> pathSep
-- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>. -- | Get all installed GHCs by reading ~/.ghcup/ghc/<dir>.
-- If a dir cannot be parsed, returns left. -- If a dir cannot be parsed, returns left.
getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath GHCTargetVersion] getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath GHCTargetVersion]
getInstalledGHCs = do getInstalledGHCs = do
ghcdir <- ghcupGHCBaseDir ghcdir <- ghcupGHCBaseDir
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory (fromGHCupPath ghcdir) fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory ghcdir
forM fs $ \f -> case parseGHCupGHCDir f of forM fs $ \f -> case parseGHCupGHCDir f of
Right r -> pure $ Right r Right r -> pure $ Right r
Left _ -> pure $ Left f Left _ -> pure $ Left f
@@ -401,10 +341,10 @@ cabalSet = do
cabalParse = MP.chunk "cabal-" *> version' cabalParse = MP.chunk "cabal-" *> version'
-- parses any path component ending with path separator, -- parses any path component ending with path separator,
-- e.g. "foo/" -- e.g. "foo/"
stripPathComponet = parseUntil1 pathSep *> MP.some pathSep stripPathComponet = parseUntil1 pathSep *> pathSep
-- parses an absolute path up until the last path separator, -- parses an absolute path up until the last path separator,
-- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo" -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"
stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet) stripAbsolutePath = pathSep *> MP.many (MP.try stripPathComponet)
-- parses a relative path up until the last path separator, -- parses a relative path up until the last path separator,
-- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo" -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"
stripRelativePath = MP.many (MP.try stripPathComponet) stripRelativePath = MP.many (MP.try stripPathComponet)
@@ -412,8 +352,7 @@ cabalSet = do
-- | Get all installed hls, by matching on -- | Get all installed hls, by matching on
-- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@, -- @~\/.ghcup\/bin/haskell-language-server-wrapper-<\hlsver\>@.
-- as well as @~\/.ghcup\/hls\/<\hlsver\>@
getInstalledHLSs :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m) getInstalledHLSs :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)
=> m [Either FilePath Version] => m [Either FilePath Version]
getInstalledHLSs = do getInstalledHLSs = do
@@ -424,7 +363,7 @@ getInstalledHLSs = do
execBlank execBlank
([s|^haskell-language-server-wrapper-.*$|] :: ByteString) ([s|^haskell-language-server-wrapper-.*$|] :: ByteString)
) )
legacy <- forM bins $ \f -> forM bins $ \f ->
case case
version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "haskell-language-server-wrapper-" f) version . T.pack <$> (stripSuffix exeExt =<< stripPrefix "haskell-language-server-wrapper-" f)
of of
@@ -432,14 +371,6 @@ getInstalledHLSs = do
Just (Left _) -> pure $ Left f Just (Left _) -> pure $ Left f
Nothing -> pure $ Left f Nothing -> pure $ Left f
hlsdir <- ghcupHLSBaseDir
fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory (fromGHCupPath hlsdir)
new <- forM fs $ \f -> case parseGHCupHLSDir f of
Right r -> pure $ Right r
Left _ -> pure $ Left f
pure (nub (new <> legacy))
-- | Get all installed stacks, by matching on -- | Get all installed stacks, by matching on
-- @~\/.ghcup\/bin/stack-<\stackver\>@. -- @~\/.ghcup\/bin/stack-<\stackver\>@.
getInstalledStacks :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m) getInstalledStacks :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m)
@@ -495,10 +426,10 @@ stackSet = do
cabalParse = MP.chunk "stack-" *> version' cabalParse = MP.chunk "stack-" *> version'
-- parses any path component ending with path separator, -- parses any path component ending with path separator,
-- e.g. "foo/" -- e.g. "foo/"
stripPathComponet = parseUntil1 pathSep *> MP.some pathSep stripPathComponet = parseUntil1 pathSep *> pathSep
-- parses an absolute path up until the last path separator, -- parses an absolute path up until the last path separator,
-- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo" -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"
stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet) stripAbsolutePath = pathSep *> MP.many (MP.try stripPathComponet)
-- parses a relative path up until the last path separator, -- parses a relative path up until the last path separator,
-- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo" -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"
stripRelativePath = MP.many (MP.try stripPathComponet) stripRelativePath = MP.many (MP.try stripPathComponet)
@@ -515,10 +446,6 @@ hlsInstalled ver = do
vers <- fmap rights getInstalledHLSs vers <- fmap rights getInstalledHLSs
pure $ elem ver vers pure $ elem ver vers
isLegacyHLS :: (MonadIO m, MonadReader env m, HasDirs env, MonadCatch m) => Version -> m Bool
isLegacyHLS ver = do
bdir <- ghcupHLSDir ver
not <$> liftIO (doesDirectoryExist $ fromGHCupPath bdir)
-- Return the currently set hls version, if any. -- Return the currently set hls version, if any.
@@ -546,10 +473,10 @@ hlsSet = do
cabalParse = MP.chunk "haskell-language-server-wrapper-" *> version' cabalParse = MP.chunk "haskell-language-server-wrapper-" *> version'
-- parses any path component ending with path separator, -- parses any path component ending with path separator,
-- e.g. "foo/" -- e.g. "foo/"
stripPathComponet = parseUntil1 pathSep *> MP.some pathSep stripPathComponet = parseUntil1 pathSep *> pathSep
-- parses an absolute path up until the last path separator, -- parses an absolute path up until the last path separator,
-- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo" -- e.g. "/bar/baz/foo" -> "/bar/baz/", leaving "foo"
stripAbsolutePath = MP.some pathSep *> MP.many (MP.try stripPathComponet) stripAbsolutePath = pathSep *> MP.many (MP.try stripPathComponet)
-- parses a relative path up until the last path separator, -- parses a relative path up until the last path separator,
-- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo" -- e.g. "bar/baz/foo" -> "bar/baz/", leaving "foo"
stripRelativePath = MP.many (MP.try stripPathComponet) stripRelativePath = MP.many (MP.try stripPathComponet)
@@ -590,7 +517,7 @@ hlsGHCVersions' v' = do
pure . sortBy (flip compare) . rights $ vers pure . sortBy (flip compare) . rights $ vers
-- | Get all server binaries for an hls version from the ~/.ghcup/bin directory, if any. -- | Get all server binaries for an hls version, if any.
hlsServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m) hlsServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m)
=> Version => Version
-> Maybe Version -- ^ optional GHC version -> Maybe Version -- ^ optional GHC version
@@ -611,44 +538,6 @@ hlsServerBinaries ver mghcVer = do
) )
) )
-- | Get all scripts for a hls version from the ~/.ghcup/hls/<ver>/bin directory, if any.
-- Returns the full path.
hlsInternalServerScripts :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m)
=> Version
-> Maybe Version -- ^ optional GHC version
-> m [FilePath]
hlsInternalServerScripts ver mghcVer = do
dir <- ghcupHLSDir ver
let bdir = fromGHCupPath dir </> "bin"
fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)
<$> liftIO (listDirectory bdir)
-- | Get all binaries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/bin directory, if any.
-- Returns the full path.
hlsInternalServerBinaries :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)
=> Version
-> Maybe Version -- ^ optional GHC version
-> m [FilePath]
hlsInternalServerBinaries ver mghcVer = do
dir <- fromGHCupPath <$> ghcupHLSDir ver
let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)
(Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left "bin"]
fmap (bdir </>) . filter (\f -> maybe True (\gv -> ("-" <> T.unpack (prettyVer gv)) `isSuffixOf` f) mghcVer)
<$> liftIO (listDirectory bdir)
-- | Get all libraries for a hls version from the ~/.ghcup/hls/<ver>/lib/haskell-language-server-<ver>/lib/<ghc-ver>/
-- directory, if any.
-- Returns the full path.
hlsInternalServerLibs :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m, MonadFail m)
=> Version
-> Version -- ^ GHC version
-> m [FilePath]
hlsInternalServerLibs ver ghcVer = do
dir <- fromGHCupPath <$> ghcupHLSDir ver
let regex = makeRegexOpts compExtended execBlank ([s|^haskell-language-server-.*$|] :: ByteString)
(Just bdir) <- fmap headMay $ liftIO $ expandFilePath [Left (dir </> "lib"), Right regex, Left ("lib" </> T.unpack (prettyVer ghcVer))]
fmap (bdir </>) <$> liftIO (listDirectory bdir)
-- | Get the wrapper binary for an hls version, if any. -- | Get the wrapper binary for an hls version, if any.
hlsWrapperBinary :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m) hlsWrapperBinary :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m)
@@ -679,6 +568,22 @@ hlsAllBinaries ver = do
pure (maybeToList wrapper ++ hls) pure (maybeToList wrapper ++ hls)
-- | Get the active symlinks for hls.
hlsSymlinks :: (MonadReader env m, HasDirs env, MonadIO m, MonadCatch m) => m [FilePath]
hlsSymlinks = do
Dirs {..} <- getDirs
oldSyms <- liftIO $ handleIO (\_ -> pure []) $ findFiles
binDir
(makeRegexOpts compExtended
execBlank
([s|^haskell-language-server-.*$|] :: ByteString)
)
filterM
( liftIO
. pathIsLink
. (binDir </>)
)
oldSyms
@@ -726,34 +631,34 @@ getGHCForPVP pvpIn mt = do
ghcs <- rights <$> getInstalledGHCs ghcs <- rights <$> getInstalledGHCs
-- we're permissive here... failed parse just means we have no match anyway -- we're permissive here... failed parse just means we have no match anyway
let ghcs' = catMaybes $ flip fmap ghcs $ \GHCTargetVersion{..} -> do let ghcs' = catMaybes $ flip fmap ghcs $ \GHCTargetVersion{..} -> do
(pvp_, rest) <- versionToPVP _tvVersion pvp_ <- versionToPVP _tvVersion
pure (pvp_, rest, _tvTarget) pure (pvp_, _tvTarget)
getGHCForPVP' pvpIn ghcs' mt getGHCForPVP' pvpIn ghcs' mt
-- | Like 'getGHCForPVP', except with explicit input parameter. -- | Like 'getGHCForPVP', except with explicit input parameter.
-- --
-- >>> getGHCForPVP' [pver|8|] installedVersions Nothing -- >>> fmap prettyShow $ getGHCForPVP' [pver|8|] installedVersions Nothing
-- Just (GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = (Digits 8 :| []) :| [Digits 10 :| [],Digits 7 :| []], _vRel = [Str "debug" :| []], _vMeta = Just "lol"}}) -- "Just 8.10.7"
-- >>> fmap prettyShow $ getGHCForPVP' [pver|8.8|] installedVersions Nothing -- >>> fmap prettyShow $ getGHCForPVP' [pver|8.8|] installedVersions Nothing
-- "Just 8.8.4" -- "Just 8.8.4"
-- >>> fmap prettyShow $ getGHCForPVP' [pver|8.10.4|] installedVersions Nothing -- >>> fmap prettyShow $ getGHCForPVP' [pver|8.10.4|] installedVersions Nothing
-- "Just 8.10.4" -- "Just 8.10.4"
getGHCForPVP' :: MonadThrow m getGHCForPVP' :: MonadThrow m
=> PVP => PVP
-> [(PVP, Text, Maybe Text)] -- ^ installed GHCs -> [(PVP, Maybe Text)] -- ^ installed GHCs
-> Maybe Text -- ^ the target triple -> Maybe Text -- ^ the target triple
-> m (Maybe GHCTargetVersion) -> m (Maybe GHCTargetVersion)
getGHCForPVP' pvpIn ghcs' mt = do getGHCForPVP' pvpIn ghcs' mt = do
let mResult = lastMay let mResult = lastMay
. sortBy (\(x, _, _) (y, _, _) -> compare x y) . sortBy (\(x, _) (y, _) -> compare x y)
. filter . filter
(\(pvp_, _, target) -> (\(pvp_, target) ->
target == mt && matchPVPrefix pvp_ pvpIn target == mt && matchPVPrefix pvp_ pvpIn
) )
$ ghcs' $ ghcs'
forM mResult $ \(pvp_, rest, target) -> do forM mResult $ \(pvp_, target) -> do
ver' <- pvpToVersion pvp_ rest ver' <- pvpToVersion pvp_
pure (GHCTargetVersion target ver') pure (GHCTargetVersion target ver')
@@ -774,7 +679,7 @@ getLatestToolFor :: MonadThrow m
getLatestToolFor tool pvpIn dls = do getLatestToolFor tool pvpIn dls = do
let ls = fromMaybe [] $ preview (ix tool % to Map.toDescList) dls let ls = fromMaybe [] $ preview (ix tool % to Map.toDescList) dls
let ps = catMaybes $ fmap (\(v, vi) -> (,vi) <$> versionToPVP v) ls let ps = catMaybes $ fmap (\(v, vi) -> (,vi) <$> versionToPVP v) ls
pure . fmap (first fst) . headMay . filter (\((v, _), _) -> matchPVPrefix pvpIn v) $ ps pure . headMay . filter (\(v, _) -> matchPVPrefix pvpIn v) $ ps
@@ -809,7 +714,7 @@ unpackToDir dfp av = do
(untar . GZip.decompress =<< rf av) (untar . GZip.decompress =<< rf av)
| ".tar.xz" `isSuffixOf` fn -> do | ".tar.xz" `isSuffixOf` fn -> do
filecontents <- liftE $ rf av filecontents <- liftE $ rf av
let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents let decompressed = Lzma.decompress filecontents
liftE $ untar decompressed liftE $ untar decompressed
| ".tar.bz2" `isSuffixOf` fn -> | ".tar.bz2" `isSuffixOf` fn ->
liftE (untar . BZip.decompress =<< rf av) liftE (untar . BZip.decompress =<< rf av)
@@ -838,7 +743,7 @@ getArchiveFiles av = do
(entries . GZip.decompress =<< rf av) (entries . GZip.decompress =<< rf av)
| ".tar.xz" `isSuffixOf` fn -> do | ".tar.xz" `isSuffixOf` fn -> do
filecontents <- liftE $ rf av filecontents <- liftE $ rf av
let decompressed = Lzma.decompressWith (Lzma.defaultDecompressParams { Lzma.decompressAutoDecoder= True }) filecontents let decompressed = Lzma.decompress filecontents
liftE $ entries decompressed liftE $ entries decompressed
| ".tar.bz2" `isSuffixOf` fn -> | ".tar.bz2" `isSuffixOf` fn ->
liftE (entries . BZip.decompress =<< rf av) liftE (entries . BZip.decompress =<< rf av)
@@ -848,21 +753,21 @@ getArchiveFiles av = do
intoSubdir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m, MonadCatch m) intoSubdir :: (MonadReader env m, HasLog env, MonadIO m, MonadThrow m, MonadCatch m)
=> GHCupPath -- ^ unpacked tar dir => FilePath -- ^ unpacked tar dir
-> TarDir -- ^ how to descend -> TarDir -- ^ how to descend
-> Excepts '[TarDirDoesNotExist] m GHCupPath -> Excepts '[TarDirDoesNotExist] m FilePath
intoSubdir bdir tardir = case tardir of intoSubdir bdir tardir = case tardir of
RealDir pr -> do RealDir pr -> do
whenM (fmap not . liftIO . doesDirectoryExist $ fromGHCupPath (bdir `appendGHCupPath` pr)) whenM (fmap not . liftIO . doesDirectoryExist $ (bdir </> pr))
(throwE $ TarDirDoesNotExist tardir) (throwE $ TarDirDoesNotExist tardir)
pure (bdir `appendGHCupPath` pr) pure (bdir </> pr)
RegexDir r -> do RegexDir r -> do
let rs = split (`elem` pathSeparators) r let rs = split (`elem` pathSeparators) r
foldlM foldlM
(\y x -> (\y x ->
(handleIO (\_ -> pure []) . liftIO . findFiles (fromGHCupPath y) . regex $ x) >>= (\case (handleIO (\_ -> pure []) . liftIO . findFiles y . regex $ x) >>= (\case
[] -> throwE $ TarDirDoesNotExist tardir [] -> throwE $ TarDirDoesNotExist tardir
(p : _) -> pure (y `appendGHCupPath` p)) . sort (p : _) -> pure (y </> p)) . sort
) )
bdir bdir
rs rs
@@ -903,16 +808,8 @@ getLatestBaseVersion av pvpVer =
--[ Other ]-- --[ Other ]--
------------- -------------
-- | Usually @~\/.ghcup\/ghc\/\<ver\>\/bin\/@
ghcInternalBinDir :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, MonadIO m)
=> GHCTargetVersion
-> m FilePath
ghcInternalBinDir ver = do
ghcdir <- fromGHCupPath <$> ghcupGHCDir ver
pure (ghcdir </> "bin")
-- | Get tool files from @~\/.ghcup\/bin\/ghc\/\<ver\>\/bin\/\*@
-- | Get tool files from @~\/.ghcup\/ghc\/\<ver\>\/bin\/\*@
-- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix. -- while ignoring @*-\<ver\>@ symlinks and accounting for cross triple prefix.
-- --
-- Returns unversioned relative files without extension, e.g.: -- Returns unversioned relative files without extension, e.g.:
@@ -922,10 +819,11 @@ ghcToolFiles :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, Mona
=> GHCTargetVersion => GHCTargetVersion
-> Excepts '[NotInstalled] m [FilePath] -> Excepts '[NotInstalled] m [FilePath]
ghcToolFiles ver = do ghcToolFiles ver = do
bindir <- ghcInternalBinDir ver ghcdir <- lift $ ghcupGHCDir ver
let bindir = ghcdir </> "bin"
-- fail if ghc is not installed -- fail if ghc is not installed
whenM (fmap not $ ghcInstalled ver) whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir)
(throwE (NotInstalled GHC ver)) (throwE (NotInstalled GHC ver))
files <- liftIO (listDirectory bindir >>= filterM (doesFileExist . (bindir </>))) files <- liftIO (listDirectory bindir >>= filterM (doesFileExist . (bindir </>)))
@@ -957,7 +855,6 @@ make :: ( MonadThrow m
, MonadIO m , MonadIO m
, MonadReader env m , MonadReader env m
, HasDirs env , HasDirs env
, HasLog env
, HasSettings env , HasSettings env
) )
=> [String] => [String]
@@ -980,43 +877,28 @@ makeOut args workdir = do
executeOut mymake args workdir executeOut mymake args workdir
-- | Try to apply patches in order. The order is determined by -- | Try to apply patches in order. Fails with 'PatchFailed'
-- a quilt series file (in the patch directory) if one exists, -- on first failure.
-- else the patches are applied in lexicographical order.
-- Fails with 'PatchFailed' on first failure.
applyPatches :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m) applyPatches :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)
=> FilePath -- ^ dir containing patches => FilePath -- ^ dir containing patches
-> FilePath -- ^ dir to apply patches in -> FilePath -- ^ dir to apply patches in
-> Excepts '[PatchFailed] m () -> Excepts '[PatchFailed] m ()
applyPatches pdir ddir = do applyPatches pdir ddir = do
let lexicographical = (fmap . fmap) (pdir </>) $ sort <$> findFiles patches <- (fmap . fmap) (pdir </>) $ liftIO $ findFiles
pdir pdir
(makeRegexOpts compExtended (makeRegexOpts compExtended
execBlank execBlank
([s|.+\.(patch|diff)$|] :: ByteString) ([s|.+\.(patch|diff)$|] :: ByteString)
) )
let quilt = map (pdir </>) . lines <$> readFile (pdir </> "series") forM_ (sort patches) $ \patch' -> do
lift $ logInfo $ "Applying patch " <> T.pack patch'
patches <- liftIO $ quilt `catchIO` (\e -> fmap (either (const Nothing) Just)
if isDoesNotExistError e || isPermissionError e then (exec
lexicographical "patch"
else throwIO e) ["-p1", "-i", patch']
forM_ patches $ \patch' -> applyPatch patch' ddir (Just ddir)
Nothing)
!? PatchFailed
applyPatch :: (MonadReader env m, HasDirs env, HasLog env, MonadIO m)
=> FilePath -- ^ Patch
-> FilePath -- ^ dir to apply patches in
-> Excepts '[PatchFailed] m ()
applyPatch patch ddir = do
lift $ logInfo $ "Applying patch " <> T.pack patch
fmap (either (const Nothing) Just)
(exec
"patch"
["-p1", "-s", "-f", "-i", patch]
(Just ddir)
Nothing)
!? PatchFailed
-- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353 -- | https://gitlab.haskell.org/ghc/ghc/-/issues/17353
@@ -1032,8 +914,6 @@ darwinNotarization Darwin path = exec
darwinNotarization _ _ = pure $ Right () darwinNotarization _ _ = pure $ Right ()
getChangeLog :: GHCupDownloads -> Tool -> Either Version Tag -> Maybe URI getChangeLog :: GHCupDownloads -> Tool -> Either Version Tag -> Maybe URI
getChangeLog dls tool (Left v') = getChangeLog dls tool (Left v') =
preview (ix tool % ix v' % viChangeLog % _Just) dls preview (ix tool % ix v' % viChangeLog % _Just) dls
@@ -1044,6 +924,7 @@ getChangeLog dls tool (Right tag) =
-- | Execute a build action while potentially cleaning up: -- | Execute a build action while potentially cleaning up:
-- --
-- 1. the build directory, depending on the KeepDirs setting -- 1. the build directory, depending on the KeepDirs setting
-- 2. the install destination, depending on whether the build failed
runBuildAction :: ( MonadReader env m runBuildAction :: ( MonadReader env m
, HasDirs env , HasDirs env
, HasSettings env , HasSettings env
@@ -1054,12 +935,15 @@ runBuildAction :: ( MonadReader env m
, MonadFail m , MonadFail m
, MonadCatch m , MonadCatch m
) )
=> GHCupPath -- ^ build directory (cleaned up depending on Settings) => FilePath -- ^ build directory (cleaned up depending on Settings)
-> Maybe FilePath -- ^ dir to *always* clean up on exception
-> Excepts e m a -> Excepts e m a
-> Excepts e m a -> Excepts e m a
runBuildAction bdir action = do runBuildAction bdir instdir action = do
Settings {..} <- lift getSettings Settings {..} <- lift getSettings
let exAction = do let exAction = do
forM_ instdir $ \dir ->
hideError doesNotExistErrorType $ recyclePathForcibly dir
when (keepDirs == Never) when (keepDirs == Never)
$ rmBDir bdir $ rmBDir bdir
v <- v <-
@@ -1081,7 +965,7 @@ cleanUpOnError :: ( MonadReader env m
, MonadFail m , MonadFail m
, MonadCatch m , MonadCatch m
) )
=> GHCupPath -- ^ build directory (cleaned up depending on Settings) => FilePath -- ^ build directory (cleaned up depending on Settings)
-> Excepts e m a -> Excepts e m a
-> Excepts e m a -> Excepts e m a
cleanUpOnError bdir action = do cleanUpOnError bdir action = do
@@ -1090,33 +974,13 @@ cleanUpOnError bdir action = do
flip onException (lift exAction) $ onE_ exAction action flip onException (lift exAction) $ onE_ exAction action
-- | Clean up the given directory if the action fails,
-- depending on the Settings.
cleanFinally :: ( MonadReader env m
, HasDirs env
, HasSettings env
, MonadIO m
, MonadMask m
, HasLog env
, MonadUnliftIO m
, MonadFail m
, MonadCatch m
)
=> GHCupPath -- ^ build directory (cleaned up depending on Settings)
-> Excepts e m a
-> Excepts e m a
cleanFinally bdir action = do
Settings {..} <- lift getSettings
let exAction = when (keepDirs == Never) $ rmBDir bdir
flip finally (lift exAction) $ onE_ exAction action
-- | Remove a build directory, ignoring if it doesn't exist and gracefully -- | Remove a build directory, ignoring if it doesn't exist and gracefully
-- printing other errors without crashing. -- printing other errors without crashing.
rmBDir :: (MonadReader env m, HasLog env, MonadUnliftIO m, MonadIO m) => GHCupPath -> m () rmBDir :: (MonadReader env m, HasLog env, MonadUnliftIO m, MonadIO m) => FilePath -> m ()
rmBDir dir = withRunInIO (\run -> run $ rmBDir dir = withRunInIO (\run -> run $
liftIO $ handleIO (\e -> run $ logWarn $ liftIO $ handleIO (\e -> run $ logWarn $
"Couldn't remove build dir " <> T.pack (fromGHCupPath dir) <> ", error was: " <> T.pack (displayException e)) "Couldn't remove build dir " <> T.pack dir <> ", error was: " <> T.pack (displayException e))
$ hideError doesNotExistErrorType $ hideError doesNotExistErrorType
$ rmPathForcibly dir) $ rmPathForcibly dir)
@@ -1136,17 +1000,50 @@ getVersionInfo v' tool =
-- | The file extension for executables. -- | The file extension for executables.
exeExt :: String exeExt :: String
exeExt #if defined(IS_WINDOWS)
| isWindows = ".exe" exeExt = ".exe"
| otherwise = "" #else
exeExt = ""
#endif
-- | The file extension for executables. -- | The file extension for executables.
exeExt' :: ByteString exeExt' :: ByteString
exeExt' #if defined(IS_WINDOWS)
| isWindows = ".exe" exeExt' = ".exe"
| otherwise = "" #else
exeExt' = ""
#endif
-- | Enables ANSI support on windows, does nothing on unix.
--
-- Returns 'Left str' on errors and 'Right bool' on success, where
-- 'bool' markes whether ansi support was already enabled.
--
-- This function never crashes.
--
-- Rip-off of https://docs.rs/ansi_term/0.12.1/x86_64-pc-windows-msvc/src/ansi_term/windows.rs.html#10-61
enableAnsiSupport :: IO (Either String Bool)
#if defined(IS_WINDOWS)
enableAnsiSupport = handleIO (pure . Left . displayException) $ do
-- ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
-- Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
h <- createFile "CONOUT$" (gENERIC_WRITE .|. gENERIC_READ)
fILE_SHARE_WRITE Nothing oPEN_EXISTING 0 Nothing
when (h == iNVALID_HANDLE_VALUE ) $ fail "invalid handle value"
-- ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
m <- getConsoleMode h
-- VT processing not already enabled?
if ((m .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
-- https://docs.microsoft.com/en-us/windows/console/setconsolemode
then setConsoleMode h (m .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING)
>> pure (Right False)
else pure (Right True)
#else
enableAnsiSupport = pure (Right True)
#endif
-- | On unix, we can use symlinks, so we just get the -- | On unix, we can use symlinks, so we just get the
@@ -1155,27 +1052,33 @@ exeExt'
-- On windows, we have to emulate symlinks via shims, -- On windows, we have to emulate symlinks via shims,
-- see 'createLink'. -- see 'createLink'.
getLinkTarget :: FilePath -> IO FilePath getLinkTarget :: FilePath -> IO FilePath
getLinkTarget fp getLinkTarget fp = do
| isWindows = do #if defined(IS_WINDOWS)
content <- readFile (dropExtension fp <.> "shim") content <- readFile (dropExtension fp <.> "shim")
[p] <- pure . filter ("path = " `isPrefixOf`) . lines $ content [p] <- pure . filter ("path = " `isPrefixOf`) . lines $ content
pure $ stripNewline $ dropPrefix "path = " p pure $ stripNewline $ dropPrefix "path = " p
| otherwise = getSymbolicLinkTarget fp #else
getSymbolicLinkTarget fp
#endif
-- | Checks whether the path is a link. -- | Checks whether the path is a link.
pathIsLink :: FilePath -> IO Bool pathIsLink :: FilePath -> IO Bool
pathIsLink fp #if defined(IS_WINDOWS)
| isWindows = doesPathExist (dropExtension fp <.> "shim") pathIsLink fp = doesPathExist (dropExtension fp <.> "shim")
| otherwise = pathIsSymbolicLink fp #else
pathIsLink = pathIsSymbolicLink
#endif
rmLink :: (MonadReader env m, HasDirs env, MonadIO m, MonadMask m) => FilePath -> m () rmLink :: (MonadReader env m, HasDirs env, MonadIO m, MonadMask m) => FilePath -> m ()
rmLink fp #if defined(IS_WINDOWS)
| isWindows = do rmLink fp = do
hideError doesNotExistErrorType . recycleFile $ fp hideError doesNotExistErrorType . recycleFile $ fp
hideError doesNotExistErrorType . recycleFile $ (dropExtension fp <.> "shim") hideError doesNotExistErrorType . recycleFile $ (dropExtension fp <.> "shim")
| otherwise = hideError doesNotExistErrorType . recycleFile $ fp #else
rmLink = hideError doesNotExistErrorType . recycleFile
#endif
-- | Creates a symbolic link on unix and a fake symlink on windows for -- | Creates a symbolic link on unix and a fake symlink on windows for
@@ -1199,30 +1102,31 @@ createLink :: ( MonadMask m
=> FilePath -- ^ path to the target executable => FilePath -- ^ path to the target executable
-> FilePath -- ^ path to be created -> FilePath -- ^ path to be created
-> m () -> m ()
createLink link exe createLink link exe = do
| isWindows = do #if defined(IS_WINDOWS)
dirs <- getDirs dirs <- getDirs
let shimGen = fromGHCupPath (cacheDir dirs) </> "gs.exe" let shimGen = cacheDir dirs </> "gs.exe"
let shim = dropExtension exe <.> "shim" let shim = dropExtension exe <.> "shim"
-- For hardlinks, link needs to be absolute. -- For hardlinks, link needs to be absolute.
-- If link is relative, it's relative to the target exe. -- If link is relative, it's relative to the target exe.
-- Note that (</>) drops lhs when rhs is absolute. -- Note that (</>) drops lhs when rhs is absolute.
fullLink = takeDirectory exe </> link fullLink = takeDirectory exe </> link
shimContents = "path = " <> fullLink shimContents = "path = " <> fullLink
logDebug $ "rm -f " <> T.pack exe logDebug $ "rm -f " <> T.pack exe
rmLink exe rmLink exe
logDebug $ "ln -s " <> T.pack fullLink <> " " <> T.pack exe logDebug $ "ln -s " <> T.pack fullLink <> " " <> T.pack exe
liftIO $ copyFile shimGen exe False liftIO $ copyFile shimGen exe
liftIO $ writeFile shim shimContents liftIO $ writeFile shim shimContents
| otherwise = do #else
logDebug $ "rm -f " <> T.pack exe logDebug $ "rm -f " <> T.pack exe
hideError doesNotExistErrorType $ recycleFile exe hideError doesNotExistErrorType $ recycleFile exe
logDebug $ "ln -s " <> T.pack link <> " " <> T.pack exe logDebug $ "ln -s " <> T.pack link <> " " <> T.pack exe
liftIO $ createFileLink link exe liftIO $ createFileLink link exe
#endif
ensureGlobalTools :: ( MonadMask m ensureGlobalTools :: ( MonadMask m
@@ -1237,80 +1141,45 @@ ensureGlobalTools :: ( MonadMask m
, MonadFail m , MonadFail m
) )
=> Excepts '[GPGError, DigestError , DownloadFailed, NoDownload] m () => Excepts '[GPGError, DigestError , DownloadFailed, NoDownload] m ()
ensureGlobalTools ensureGlobalTools = do
| isWindows = do #if defined(IS_WINDOWS)
(GHCupInfo _ _ gTools) <- lift getGHCupInfo (GHCupInfo _ _ gTools) <- lift getGHCupInfo
dirs <- lift getDirs dirs <- lift getDirs
shimDownload <- liftE $ lE @_ @'[NoDownload] shimDownload <- liftE $ lE @_ @'[NoDownload]
$ maybe (Left NoDownload) Right $ Map.lookup ShimGen gTools $ maybe (Left NoDownload) Right $ Map.lookup ShimGen gTools
let dl = downloadCached' shimDownload (Just "gs.exe") Nothing let dl = downloadCached' shimDownload (Just "gs.exe") Nothing
void $ (\DigestError{} -> do void $ (\(DigestError _ _ _) -> do
lift $ logWarn "Digest doesn't match, redownloading gs.exe..." lift $ logWarn "Digest doesn't match, redownloading gs.exe..."
lift $ logDebug ("rm -f " <> T.pack (fromGHCupPath (cacheDir dirs) </> "gs.exe")) lift $ logDebug ("rm -f " <> T.pack (cacheDir dirs </> "gs.exe"))
lift $ hideError doesNotExistErrorType $ recycleFile (fromGHCupPath (cacheDir dirs) </> "gs.exe") lift $ hideError doesNotExistErrorType $ recycleFile (cacheDir dirs </> "gs.exe")
liftE @'[GPGError, DigestError , DownloadFailed] $ dl liftE @'[GPGError, DigestError , DownloadFailed] $ dl
) `catchE` liftE @'[GPGError, DigestError , DownloadFailed] dl ) `catchE` (liftE @'[GPGError, DigestError , DownloadFailed] dl)
| otherwise = pure () pure ()
#else
pure ()
#endif
-- | Ensure ghcup directory structure exists. -- | Ensure ghcup directory structure exists.
ensureDirectories :: Dirs -> IO () ensureDirectories :: Dirs -> IO ()
ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir) = do ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir) = do
createDirRecursive' (fromGHCupPath baseDir) createDirRecursive' baseDir
createDirRecursive' (fromGHCupPath baseDir </> "ghc") createDirRecursive' (baseDir </> "ghc")
createDirRecursive' binDir createDirRecursive' binDir
createDirRecursive' (fromGHCupPath cacheDir) createDirRecursive' cacheDir
createDirRecursive' (fromGHCupPath logsDir) createDirRecursive' logsDir
createDirRecursive' (fromGHCupPath confDir) createDirRecursive' confDir
createDirRecursive' (fromGHCupPath trashDir) createDirRecursive' trashDir
createDirRecursive' (fromGHCupPath dbDir)
pure () pure ()
-- | For ghc without arch triple, this is: -- | For ghc without arch triple, this is:
-- --
-- - ghc -- - ghc-<ver> (e.g. ghc-8.10.4)
-- --
-- For ghc with arch triple: -- For ghc with arch triple:
-- --
-- - <triple>-ghc (e.g. arm-linux-gnueabihf-ghc) -- - <triple>-ghc-<ver> (e.g. arm-linux-gnueabihf-ghc-8.10.4)
ghcBinaryName :: GHCTargetVersion -> String ghcBinaryName :: GHCTargetVersion -> String
ghcBinaryName (GHCTargetVersion (Just t) _) = T.unpack (t <> "-ghc" <> T.pack exeExt) ghcBinaryName (GHCTargetVersion (Just t) v') = T.unpack (t <> "-ghc-" <> prettyVer v' <> T.pack exeExt)
ghcBinaryName (GHCTargetVersion Nothing _) = T.unpack ("ghc" <> T.pack exeExt) ghcBinaryName (GHCTargetVersion Nothing v') = T.unpack ("ghc-" <> prettyVer v' <> T.pack exeExt)
-- | Does basic checks for isolated installs
-- Isolated Directory:
-- 1. if it doesn't exist -> proceed
-- 2. if it exists and is empty -> proceed
-- 3. if it exists and is non-empty -> panic and leave the house
installDestSanityCheck :: ( MonadIO m
, MonadCatch m
, MonadMask m
) =>
InstallDirResolved ->
Excepts '[DirNotEmpty] m ()
installDestSanityCheck (IsolateDirResolved isoDir) = do
hideErrorDef [doesNotExistErrorType] () $ do
empty' <- liftIO $ S.null $ getDirectoryContentsRecursiveUnsafe isoDir
when (not empty') (throwE $ DirNotEmpty isoDir)
installDestSanityCheck _ = pure ()
-- | Returns 'Nothing' for legacy installs.
getInstalledFiles :: ( MonadIO m
, MonadCatch m
, MonadReader env m
, HasDirs env
, MonadFail m
)
=> Tool
-> GHCTargetVersion
-> m (Maybe [FilePath])
getInstalledFiles t v' = hideErrorDef [doesNotExistErrorType] Nothing $ do
f <- recordedInstallationFile t v'
(force -> !c) <- liftIO
(readFile f >>= evaluate)
pure (Just $ lines c)

View File

@@ -3,7 +3,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE QuasiQuotes #-}
{-| {-|
Module : GHCup.Utils.Dirs Module : GHCup.Utils.Dirs
@@ -21,84 +20,15 @@ module GHCup.Utils.Dirs
, ghcupCacheDir , ghcupCacheDir
, ghcupGHCBaseDir , ghcupGHCBaseDir
, ghcupGHCDir , ghcupGHCDir
, ghcupHLSBaseDir
, ghcupHLSDir
, mkGhcupTmpDir , mkGhcupTmpDir
, parseGHCupGHCDir , parseGHCupGHCDir
, parseGHCupHLSDir
, relativeSymlink , relativeSymlink
, withGHCupTmpDir , withGHCupTmpDir
, getConfigFilePath , getConfigFilePath
#if !defined(IS_WINDOWS)
, useXDG , useXDG
#endif
, cleanupTrash , cleanupTrash
, GHCupPath
, appendGHCupPath
, fromGHCupPath
, createTempGHCupDirectory
, getGHCupTmpDirs
, removeDirectory
, removeDirectoryRecursive
, removePathForcibly
-- System.Directory re-exports
, createDirectory
, createDirectoryIfMissing
, renameDirectory
, listDirectory
, getDirectoryContents
, getCurrentDirectory
, setCurrentDirectory
, withCurrentDirectory
, getHomeDirectory
, XdgDirectory(..)
, getXdgDirectory
, XdgDirectoryList(..)
, getXdgDirectoryList
, getAppUserDataDirectory
, getUserDocumentsDirectory
, getTemporaryDirectory
, removeFile
, renameFile
, renamePath
, getFileSize
, canonicalizePath
, makeAbsolute
, makeRelativeToCurrentDirectory
, doesPathExist
, doesFileExist
, doesDirectoryExist
, findExecutable
, findExecutables
, findExecutablesInDirectories
, findFile
, findFileWith
, findFilesWith
, exeExtension
, createFileLink
, createDirectoryLink
, removeDirectoryLink
, pathIsSymbolicLink
, getSymbolicLinkTarget
, Permissions
, emptyPermissions
, readable
, writable
, executable
, searchable
, setOwnerReadable
, setOwnerWritable
, setOwnerExecutable
, setOwnerSearchable
, getPermissions
, setPermissions
, copyPermissions
, getAccessTime
, getModificationTime
, setAccessTime
, setModificationTime
, isSymbolicLink
) )
where where
@@ -110,80 +40,31 @@ import GHCup.Types.Optics
import GHCup.Utils.MegaParsec import GHCup.Utils.MegaParsec
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import GHCup.Utils.File.Common
import GHCup.Utils.String.QQ
import Control.DeepSeq (NFData, rnf)
import Control.Exception.Safe import Control.Exception.Safe
import Control.Monad import Control.Monad
import Control.Monad.IO.Unlift import Control.Monad.IO.Unlift
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Resource hiding (throwM) import Control.Monad.Trans.Resource hiding (throwM)
import Data.List
import Data.ByteString ( ByteString )
import Data.Bifunctor import Data.Bifunctor
import Data.Maybe import Data.Maybe
import Data.Versions
import GHC.IO.Exception ( IOErrorType(NoSuchThing) ) import GHC.IO.Exception ( IOErrorType(NoSuchThing) )
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Optics import Optics
import Safe import System.Directory
import System.Directory hiding ( removeDirectory
, removeDirectoryRecursive
, removePathForcibly
, findFiles
)
import qualified System.Directory as SD
import System.DiskSpace import System.DiskSpace
import System.Environment import System.Environment
import System.FilePath import System.FilePath
import System.IO.Temp import System.IO.Temp
import Text.Regex.Posix
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Yaml.Aeson as Y import qualified Data.YAML.Aeson as Y
import qualified Text.Megaparsec as MP import qualified Text.Megaparsec as MP
import Control.Concurrent (threadDelay) import Control.Concurrent (threadDelay)
---------------------------
--[ GHCupPath utilities ]--
---------------------------
-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.
--
-- The constructor is not exported.
newtype GHCupPath = GHCupPath FilePath
deriving (Show, Eq, Ord)
instance NFData GHCupPath where
rnf (GHCupPath fp) = rnf fp
appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath
appendGHCupPath (GHCupPath gp) fp = GHCupPath (gp </> fp)
fromGHCupPath :: GHCupPath -> FilePath
fromGHCupPath (GHCupPath gp) = gp
createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath
createTempGHCupDirectory (GHCupPath gp) d = GHCupPath <$> createTempDirectory gp d
getGHCupTmpDirs :: IO [GHCupPath]
getGHCupTmpDirs = do
tmpdir <- getCanonicalTemporaryDirectory
ghcup_dirs <- handleIO (\_ -> pure []) $ findFiles
tmpdir
(makeRegexOpts compExtended
execBlank
([s|^ghcup-.*$|] :: ByteString)
)
pure (fmap (\p -> GHCupPath (tmpdir </> p)) $ filter (maybe False ("ghcup-" `isPrefixOf`) . lastMay . splitPath) ghcup_dirs)
------------------------------ ------------------------------
--[ GHCup base directories ]-- --[ GHCup base directories ]--
------------------------------ ------------------------------
@@ -193,134 +74,123 @@ getGHCupTmpDirs = do
-- --
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything), -- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec. -- then uses 'XDG_DATA_HOME/ghcup' as per xdg spec.
ghcupBaseDir :: IO GHCupPath ghcupBaseDir :: IO FilePath
ghcupBaseDir ghcupBaseDir = do
| isWindows = do #if defined(IS_WINDOWS)
bdir <- fromMaybe "C:\\" <$> lookupEnv "GHCUP_INSTALL_BASE_PREFIX" bdir <- fromMaybe "C:\\" <$> lookupEnv "GHCUP_INSTALL_BASE_PREFIX"
pure (GHCupPath (bdir </> "ghcup")) pure (bdir </> "ghcup")
| otherwise = do #else
xdg <- useXDG xdg <- useXDG
if xdg if xdg
then do then do
bdir <- lookupEnv "XDG_DATA_HOME" >>= \case bdir <- lookupEnv "XDG_DATA_HOME" >>= \case
Just r -> pure r Just r -> pure r
Nothing -> do Nothing -> do
home <- liftIO getHomeDirectory home <- liftIO getHomeDirectory
pure (home </> ".local" </> "share") pure (home </> ".local" </> "share")
pure (GHCupPath (bdir </> "ghcup")) pure (bdir </> "ghcup")
else do else do
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
Just r -> pure r Just r -> pure r
Nothing -> liftIO getHomeDirectory Nothing -> liftIO getHomeDirectory
pure (GHCupPath (bdir </> ".ghcup")) pure (bdir </> ".ghcup")
#endif
-- | ~/.ghcup by default -- | ~/.ghcup by default
-- --
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything), -- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec. -- then uses 'XDG_CONFIG_HOME/ghcup' as per xdg spec.
ghcupConfigDir :: IO GHCupPath ghcupConfigDir :: IO FilePath
ghcupConfigDir ghcupConfigDir = do
| isWindows = ghcupBaseDir #if defined(IS_WINDOWS)
| otherwise = do ghcupBaseDir
xdg <- useXDG #else
if xdg xdg <- useXDG
then do if xdg
bdir <- lookupEnv "XDG_CONFIG_HOME" >>= \case then do
Just r -> pure r bdir <- lookupEnv "XDG_CONFIG_HOME" >>= \case
Nothing -> do Just r -> pure r
home <- liftIO getHomeDirectory Nothing -> do
pure (home </> ".config") home <- liftIO getHomeDirectory
pure (GHCupPath (bdir </> "ghcup")) pure (home </> ".config")
else do pure (bdir </> "ghcup")
bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case else do
Just r -> pure r bdir <- lookupEnv "GHCUP_INSTALL_BASE_PREFIX" >>= \case
Nothing -> liftIO getHomeDirectory Just r -> pure r
pure (GHCupPath (bdir </> ".ghcup")) Nothing -> liftIO getHomeDirectory
pure (bdir </> ".ghcup")
#endif
-- | If 'GHCUP_USE_XDG_DIRS' is set (to anything), -- | If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin' -- then uses 'XDG_BIN_HOME' env var or defaults to '~/.local/bin'
-- (which, sadly is not strictly xdg spec). -- (which, sadly is not strictly xdg spec).
ghcupBinDir :: IO FilePath ghcupBinDir :: IO FilePath
ghcupBinDir ghcupBinDir = do
| isWindows = (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin") #if defined(IS_WINDOWS)
| otherwise = do ghcupBaseDir <&> (</> "bin")
xdg <- useXDG #else
if xdg xdg <- useXDG
then do if xdg
lookupEnv "XDG_BIN_HOME" >>= \case then do
Just r -> pure r lookupEnv "XDG_BIN_HOME" >>= \case
Nothing -> do Just r -> pure r
home <- liftIO getHomeDirectory Nothing -> do
pure (home </> ".local" </> "bin") home <- liftIO getHomeDirectory
else (fromGHCupPath <$> ghcupBaseDir) <&> (</> "bin") pure (home </> ".local" </> "bin")
else ghcupBaseDir <&> (</> "bin")
#endif
-- | Defaults to '~/.ghcup/cache'. -- | Defaults to '~/.ghcup/cache'.
-- --
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything), -- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec. -- then uses 'XDG_CACHE_HOME/ghcup' as per xdg spec.
ghcupCacheDir :: IO GHCupPath ghcupCacheDir :: IO FilePath
ghcupCacheDir ghcupCacheDir = do
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache")) #if defined(IS_WINDOWS)
| otherwise = do ghcupBaseDir <&> (</> "cache")
xdg <- useXDG #else
if xdg xdg <- useXDG
then do if xdg
bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case then do
Just r -> pure r bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case
Nothing -> do Just r -> pure r
home <- liftIO getHomeDirectory Nothing -> do
pure (home </> ".cache") home <- liftIO getHomeDirectory
pure (GHCupPath (bdir </> "ghcup")) pure (home </> ".cache")
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "cache")) pure (bdir </> "ghcup")
else ghcupBaseDir <&> (</> "cache")
#endif
-- | Defaults to '~/.ghcup/logs'. -- | Defaults to '~/.ghcup/logs'.
-- --
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything), -- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec. -- then uses 'XDG_CACHE_HOME/ghcup/logs' as per xdg spec.
ghcupLogsDir :: IO GHCupPath ghcupLogsDir :: IO FilePath
ghcupLogsDir ghcupLogsDir = do
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs")) #if defined(IS_WINDOWS)
| otherwise = do ghcupBaseDir <&> (</> "logs")
xdg <- useXDG #else
if xdg xdg <- useXDG
then do if xdg
bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case then do
Just r -> pure r bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case
Nothing -> do Just r -> pure r
home <- liftIO getHomeDirectory Nothing -> do
pure (home </> ".cache") home <- liftIO getHomeDirectory
pure (GHCupPath (bdir </> "ghcup" </> "logs")) pure (home </> ".cache")
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "logs")) pure (bdir </> "ghcup" </> "logs")
else ghcupBaseDir <&> (</> "logs")
#endif
-- | Defaults to '~/.ghcup/db.
--
-- If 'GHCUP_USE_XDG_DIRS' is set (to anything),
-- then uses 'XDG_CACHE_HOME/ghcup/db as per xdg spec.
ghcupDbDir :: IO GHCupPath
ghcupDbDir
| isWindows = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))
| otherwise = do
xdg <- useXDG
if xdg
then do
bdir <- lookupEnv "XDG_CACHE_HOME" >>= \case
Just r -> pure r
Nothing -> do
home <- liftIO getHomeDirectory
pure (home </> ".cache")
pure (GHCupPath (bdir </> "ghcup" </> "db"))
else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "db"))
-- | '~/.ghcup/trash'. -- | '~/.ghcup/trash'.
-- Mainly used on windows to improve file removal operations -- Mainly used on windows to improve file removal operations
ghcupRecycleDir :: IO GHCupPath ghcupRecycleDir :: IO FilePath
ghcupRecycleDir = ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp </> "trash")) ghcupRecycleDir = ghcupBaseDir <&> (</> "trash")
@@ -332,7 +202,6 @@ getAllDirs = do
logsDir <- ghcupLogsDir logsDir <- ghcupLogsDir
confDir <- ghcupConfigDir confDir <- ghcupConfigDir
recycleDir <- ghcupRecycleDir recycleDir <- ghcupRecycleDir
dbDir <- ghcupDbDir
pure Dirs { .. } pure Dirs { .. }
@@ -344,7 +213,7 @@ getAllDirs = do
getConfigFilePath :: (MonadIO m) => m FilePath getConfigFilePath :: (MonadIO m) => m FilePath
getConfigFilePath = do getConfigFilePath = do
confDir <- liftIO ghcupConfigDir confDir <- liftIO ghcupConfigDir
pure $ fromGHCupPath confDir </> "config.yaml" pure $ confDir </> "config.yaml"
ghcupConfigFile :: (MonadIO m) ghcupConfigFile :: (MonadIO m)
=> Excepts '[JSONError] m UserSettings => Excepts '[JSONError] m UserSettings
@@ -353,7 +222,7 @@ ghcupConfigFile = do
contents <- liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ Just <$> BS.readFile filepath contents <- liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ Just <$> BS.readFile filepath
case contents of case contents of
Nothing -> pure defaultUserSettings Nothing -> pure defaultUserSettings
Just contents' -> lE' JSONDecodeError . first displayException . Y.decodeEither' $ contents' Just contents' -> lE' JSONDecodeError . first snd . Y.decode1Strict $ contents'
------------------------- -------------------------
@@ -362,10 +231,10 @@ ghcupConfigFile = do
-- | ~/.ghcup/ghc by default. -- | ~/.ghcup/ghc by default.
ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath ghcupGHCBaseDir :: (MonadReader env m, HasDirs env) => m FilePath
ghcupGHCBaseDir = do ghcupGHCBaseDir = do
Dirs {..} <- getDirs Dirs {..} <- getDirs
pure (baseDir `appendGHCupPath` "ghc") pure (baseDir </> "ghc")
-- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'. -- | Gets '~/.ghcup/ghc/<ghcupGHCDir>'.
@@ -374,11 +243,11 @@ ghcupGHCBaseDir = do
-- * 8.8.4 -- * 8.8.4
ghcupGHCDir :: (MonadReader env m, HasDirs env, MonadThrow m) ghcupGHCDir :: (MonadReader env m, HasDirs env, MonadThrow m)
=> GHCTargetVersion => GHCTargetVersion
-> m GHCupPath -> m FilePath
ghcupGHCDir ver = do ghcupGHCDir ver = do
ghcbasedir <- ghcupGHCBaseDir ghcbasedir <- ghcupGHCBaseDir
let verdir = T.unpack $ tVerToText ver let verdir = T.unpack $ tVerToText ver
pure (ghcbasedir `appendGHCupPath` verdir) pure (ghcbasedir </> verdir)
-- | See 'ghcupToolParser'. -- | See 'ghcupToolParser'.
@@ -386,24 +255,6 @@ parseGHCupGHCDir :: MonadThrow m => FilePath -> m GHCTargetVersion
parseGHCupGHCDir (T.pack -> fp) = parseGHCupGHCDir (T.pack -> fp) =
throwEither $ MP.parse ghcTargetVerP "" fp throwEither $ MP.parse ghcTargetVerP "" fp
parseGHCupHLSDir :: MonadThrow m => FilePath -> m Version
parseGHCupHLSDir (T.pack -> fp) =
throwEither $ MP.parse version' "" fp
-- | ~/.ghcup/hls by default, for new-style installs.
ghcupHLSBaseDir :: (MonadReader env m, HasDirs env) => m GHCupPath
ghcupHLSBaseDir = do
Dirs {..} <- getDirs
pure (baseDir `appendGHCupPath` "hls")
-- | Gets '~/.ghcup/hls/<hls-ver>' for new-style installs.
ghcupHLSDir :: (MonadReader env m, HasDirs env, MonadThrow m)
=> Version
-> m GHCupPath
ghcupHLSDir ver = do
basedir <- ghcupHLSBaseDir
let verdir = T.unpack $ prettyVer ver
pure (basedir `appendGHCupPath` verdir)
mkGhcupTmpDir :: ( MonadReader env m mkGhcupTmpDir :: ( MonadReader env m
, HasDirs env , HasDirs env
@@ -413,8 +264,8 @@ mkGhcupTmpDir :: ( MonadReader env m
, MonadThrow m , MonadThrow m
, MonadMask m , MonadMask m
, MonadIO m) , MonadIO m)
=> m GHCupPath => m FilePath
mkGhcupTmpDir = GHCupPath <$> do mkGhcupTmpDir = do
tmpdir <- liftIO getCanonicalTemporaryDirectory tmpdir <- liftIO getCanonicalTemporaryDirectory
let minSpace = 5000 -- a rough guess, aight? let minSpace = 5000 -- a rough guess, aight?
@@ -450,14 +301,14 @@ withGHCupTmpDir :: ( MonadReader env m
, MonadThrow m , MonadThrow m
, MonadMask m , MonadMask m
, MonadIO m) , MonadIO m)
=> m GHCupPath => m FilePath
withGHCupTmpDir = snd <$> withRunInIO (\run -> withGHCupTmpDir = snd <$> withRunInIO (\run ->
run run
$ allocate $ allocate
(run mkGhcupTmpDir) (run mkGhcupTmpDir)
(\fp -> (\fp ->
handleIO (\e -> run handleIO (\e -> run
$ logDebug ("Resource cleanup failed for " <> T.pack (fromGHCupPath fp) <> ", error was: " <> T.pack (displayException e))) $ logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e)))
. rmPathForcibly . rmPathForcibly
$ fp)) $ fp))
@@ -469,23 +320,22 @@ withGHCupTmpDir = snd <$> withRunInIO (\run ->
-------------- --------------
#if !defined(IS_WINDOWS)
useXDG :: IO Bool useXDG :: IO Bool
useXDG = isJust <$> lookupEnv "GHCUP_USE_XDG_DIRS" useXDG = isJust <$> lookupEnv "GHCUP_USE_XDG_DIRS"
#endif
-- | Like 'relpath'. Assumes the inputs are resolved in case of symlinks.
relativeSymlink :: FilePath -- ^ the path in which to create the symlink relativeSymlink :: FilePath -- ^ the path in which to create the symlink
-> FilePath -- ^ the symlink destination -> FilePath -- ^ the symlink destination
-> FilePath -> FilePath
relativeSymlink p1 p2 relativeSymlink p1 p2 =
| isWindows = p2 -- windows quickly gets into MAX_PATH issues so we don't care about relative symlinks let d1 = splitDirectories p1
| otherwise = d2 = splitDirectories p2
let d1 = splitDirectories p1 common = takeWhile (\(x, y) -> x == y) $ zip d1 d2
d2 = splitDirectories p2 cPrefix = drop (length common) d1
common = takeWhile (\(x, y) -> x == y) $ zip d1 d2 in joinPath (replicate (length cPrefix) "..")
cPrefix = drop (length common) d1 <> joinPath ([pathSeparator] : drop (length common) d2)
in joinPath (replicate (length cPrefix) "..")
<> joinPath ([pathSeparator] : drop (length common) d2)
cleanupTrash :: ( MonadIO m cleanupTrash :: ( MonadIO m
@@ -498,27 +348,12 @@ cleanupTrash :: ( MonadIO m
=> m () => m ()
cleanupTrash = do cleanupTrash = do
Dirs { recycleDir } <- getDirs Dirs { recycleDir } <- getDirs
contents <- liftIO $ listDirectory (fromGHCupPath recycleDir) contents <- liftIO $ listDirectory recycleDir
if null contents if null contents
then pure () then pure ()
else do else do
logWarn ("Removing leftover files in " <> T.pack (fromGHCupPath recycleDir)) logWarn ("Removing leftover files in " <> T.pack recycleDir)
forM_ contents (\fp -> handleIO (\e -> forM_ contents (\fp -> handleIO (\e ->
logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e)) logDebug ("Resource cleanup failed for " <> T.pack fp <> ", error was: " <> T.pack (displayException e))
) $ liftIO $ removePathForcibly (recycleDir `appendGHCupPath` fp)) ) $ liftIO $ removePathForcibly (recycleDir </> fp))
-- System.Directory re-exports with GHCupPath
removeDirectory :: GHCupPath -> IO ()
removeDirectory (GHCupPath fp) = SD.removeDirectory fp
removeDirectoryRecursive :: GHCupPath -> IO ()
removeDirectoryRecursive (GHCupPath fp) = SD.removeDirectoryRecursive fp
removePathForcibly :: GHCupPath -> IO ()
removePathForcibly (GHCupPath fp) = SD.removePathForcibly fp

View File

@@ -1,37 +0,0 @@
module GHCup.Utils.Dirs
( GHCupPath
, appendGHCupPath
, fromGHCupPath
, createTempGHCupDirectory
, removeDirectory
, removeDirectoryRecursive
, removePathForcibly
)
where
import Control.DeepSeq (NFData)
-- | A 'GHCupPath' is a safe sub-path that can be recursively deleted.
newtype GHCupPath = GHCupPath FilePath
instance Show GHCupPath where
instance Eq GHCupPath where
instance Ord GHCupPath where
instance NFData GHCupPath where
appendGHCupPath :: GHCupPath -> FilePath -> GHCupPath
fromGHCupPath :: GHCupPath -> FilePath
createTempGHCupDirectory :: GHCupPath -> FilePath -> IO GHCupPath
removeDirectory :: GHCupPath -> IO ()
removeDirectoryRecursive :: GHCupPath -> IO ()
removePathForcibly :: GHCupPath -> IO ()

View File

@@ -1,160 +1,17 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module GHCup.Utils.File ( module GHCup.Utils.File (
mergeFileTree,
copyFileE,
findFilesDeep,
getDirectoryContentsRecursive,
getDirectoryContentsRecursiveBFS,
getDirectoryContentsRecursiveDFS,
getDirectoryContentsRecursiveUnsafe,
getDirectoryContentsRecursiveBFSUnsafe,
getDirectoryContentsRecursiveDFSUnsafe,
recordedInstallationFile,
module GHCup.Utils.File.Common, module GHCup.Utils.File.Common,
#if IS_WINDOWS
executeOut, module GHCup.Utils.File.Windows
execLogged, #else
exec, module GHCup.Utils.File.Posix
toProcessError, #endif
chmod_755,
isBrokenSymlink,
copyFile,
deleteFile,
install,
removeEmptyDirectory,
) where ) where
import GHCup.Utils.Dirs
import GHCup.Utils.File.Common import GHCup.Utils.File.Common
#if IS_WINDOWS #if IS_WINDOWS
import GHCup.Utils.File.Windows import GHCup.Utils.File.Windows
#else #else
import GHCup.Utils.File.Posix import GHCup.Utils.File.Posix
#endif #endif
import GHCup.Errors
import GHCup.Types
import GHCup.Types.Optics
import GHCup.Utils.Prelude
import Text.Regex.Posix
import Control.Exception.Safe
import Haskus.Utils.Variant.Excepts
import Control.Monad.Reader
import System.FilePath
import Text.PrettyPrint.HughesPJClass (prettyShow)
import qualified Data.Text as T
import qualified Streamly.Prelude as S
mergeFileTree :: (MonadMask m, S.MonadAsync m, MonadReader env m, HasDirs env)
=> GHCupPath -- ^ source base directory from which to install findFiles
-> InstallDirResolved -- ^ destination base dir
-> Tool
-> GHCTargetVersion
-> (FilePath -> FilePath -> m ()) -- ^ file copy operation
-> m ()
mergeFileTree sourceBase destBase tool v' copyOp = do
-- These checks are not atomic, but we perform them to have
-- the opportunity to abort before copying has started.
--
-- The actual copying might still fail.
liftIO $ baseCheck (fromGHCupPath sourceBase)
liftIO $ destCheck (fromInstallDir destBase)
recFile <- recordedInstallationFile tool v'
case destBase of
IsolateDirResolved _ -> pure ()
_ -> do
whenM (liftIO $ doesFileExist recFile) $ throwIO $ userError ("mergeFileTree: DB file " <> recFile <> " already exists!")
liftIO $ createDirectoryIfMissing True (takeDirectory recFile)
flip S.mapM_ (getDirectoryContentsRecursive sourceBase) $ \f -> do
copy f
recordInstalledFile f recFile
pure f
where
recordInstalledFile f recFile = do
case destBase of
IsolateDirResolved _ -> pure ()
_ -> liftIO $ appendFile recFile (f <> "\n")
copy source = do
let dest = fromInstallDir destBase </> source
src = fromGHCupPath sourceBase </> source
when (isAbsolute source)
$ throwIO $ userError ("mergeFileTree: source file " <> source <> " is not relative!")
liftIO . createDirectoryIfMissing True . takeDirectory $ dest
copyOp src dest
baseCheck src = do
when (isRelative src)
$ throwIO $ userError ("mergeFileTree: source base directory " <> src <> " is not absolute!")
whenM (not <$> doesDirectoryExist src)
$ throwIO $ userError ("mergeFileTree: source base directory " <> src <> " does not exist!")
destCheck dest = do
when (isRelative dest)
$ throwIO $ userError ("mergeFileTree: destination base directory " <> dest <> " is not absolute!")
copyFileE :: (CopyError :< xs, MonadCatch m, MonadIO m) => FilePath -> FilePath -> Bool -> Excepts xs m ()
copyFileE from to = handleIO (throwE . CopyError . show) . liftIO . copyFile from to
-- | List all the files in a directory and all subdirectories.
--
-- The order places files in sub-directories after all the files in their
-- parent directories. The list is generated lazily so is not well defined if
-- the source directory structure changes before the list is used.
--
-- depth first
getDirectoryContentsRecursiveDFS :: (MonadCatch m, S.MonadAsync m, MonadMask m)
=> GHCupPath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveDFS (fromGHCupPath -> fp) = getDirectoryContentsRecursiveDFSUnsafe fp
-- breadth first
getDirectoryContentsRecursiveBFS :: (MonadCatch m, S.MonadAsync m, MonadMask m)
=> GHCupPath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveBFS (fromGHCupPath -> fp) = getDirectoryContentsRecursiveBFSUnsafe fp
getDirectoryContentsRecursive :: (MonadCatch m, S.MonadAsync m, MonadMask m)
=> GHCupPath
-> S.SerialT m FilePath
getDirectoryContentsRecursive = getDirectoryContentsRecursiveBFS
getDirectoryContentsRecursiveUnsafe :: (MonadCatch m, S.MonadAsync m, MonadMask m)
=> FilePath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveUnsafe = getDirectoryContentsRecursiveBFSUnsafe
findFilesDeep :: GHCupPath -> Regex -> IO [FilePath]
findFilesDeep path regex =
S.toList $ S.filter (match regex) $ getDirectoryContentsRecursive path
recordedInstallationFile :: ( MonadReader env m
, HasDirs env
)
=> Tool
-> GHCTargetVersion
-> m FilePath
recordedInstallationFile t v' = do
Dirs {..} <- getDirs
pure (fromGHCupPath dbDir </> prettyShow t </> T.unpack (tVerToText v'))

View File

@@ -1,35 +1,56 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
module GHCup.Utils.File.Common ( module GHCup.Utils.File.Common where
module GHCup.Utils.File.Common
, ProcessError(..)
, CapturedProcess(..)
) where
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import GHCup.Types(ProcessError(..), CapturedProcess(..))
import Control.Monad.Reader import Control.Monad.Reader
import Data.Maybe import Data.Maybe
import Data.Text ( Text ) import Data.Text ( Text )
import Data.Void import Data.Void
import GHC.IO.Exception import GHC.IO.Exception
import System.Directory hiding ( removeDirectory import Optics hiding ((<|), (|>))
, removeDirectoryRecursive import System.Directory
, removePathForcibly
, findFiles
)
import System.FilePath import System.FilePath
import Text.PrettyPrint.HughesPJClass hiding ( (<>) )
import Text.Regex.Posix import Text.Regex.Posix
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.ByteString.Lazy as BL
import qualified Text.Megaparsec as MP import qualified Text.Megaparsec as MP
data ProcessError = NonZeroExit Int FilePath [String]
| PTerminated FilePath [String]
| PStopped FilePath [String]
| NoSuchPid FilePath [String]
deriving Show
instance Pretty ProcessError where
pPrint (NonZeroExit e exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "failed with exit code" <+> text (show e <> ".")
pPrint (PTerminated exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "terminated."
pPrint (PStopped exe args) =
text "Process" <+> pPrint exe <+> text "with arguments" <+> pPrint args <+> text "stopped."
pPrint (NoSuchPid exe args) =
text "Could not find PID for process running " <+> pPrint exe <+> text " with arguments " <+> text (show args) <+> text "."
data CapturedProcess = CapturedProcess
{ _exitCode :: ExitCode
, _stdOut :: BL.ByteString
, _stdErr :: BL.ByteString
}
deriving (Eq, Show)
makeLenses ''CapturedProcess
-- | Search for a file in the search paths. -- | Search for a file in the search paths.
-- --
-- Catches `PermissionDenied` and `NoSuchThing` and returns `Nothing`. -- Catches `PermissionDenied` and `NoSuchThing` and returns `Nothing`.
@@ -79,26 +100,15 @@ isInPath p = do
else pure False else pure False
-- | Follows the first match in case of Regex.
expandFilePath :: [Either FilePath Regex] -> IO [FilePath]
expandFilePath = go ""
where
go :: FilePath -> [Either FilePath Regex] -> IO [FilePath]
go p [] = pure [p]
go p (x:xs) = do
case x of
Left s -> go (p </> s) xs
Right regex -> do
fps <- findFiles p regex
res <- forM fps $ \fp -> go (p </> fp) xs
pure $ mconcat res
findFiles :: FilePath -> Regex -> IO [FilePath] findFiles :: FilePath -> Regex -> IO [FilePath]
findFiles path regex = do findFiles path regex = do
contents <- listDirectory path contents <- listDirectory path
pure $ filter (match regex) contents pure $ filter (match regex) contents
findFilesDeep :: FilePath -> Regex -> IO [FilePath]
findFilesDeep path regex = do
contents <- getDirectoryContentsRecursive path
pure $ filter (match regex) contents
findFiles' :: FilePath -> MP.Parsec Void Text a -> IO [FilePath] findFiles' :: FilePath -> MP.Parsec Void Text a -> IO [FilePath]
findFiles' path parser = do findFiles' path parser = do
@@ -108,5 +118,3 @@ findFiles' path parser = do
checkFileAlreadyExists :: (MonadIO m) => FilePath -> m Bool checkFileAlreadyExists :: (MonadIO m) => FilePath -> m Bool
checkFileAlreadyExists fp = liftIO $ doesFileExist fp checkFileAlreadyExists fp = liftIO $ doesFileExist fp

View File

@@ -1,7 +1,5 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiWayIf #-}
{-| {-|
Module : GHCup.Utils.File.Posix Module : GHCup.Utils.File.Posix
@@ -17,17 +15,15 @@ Some of these functions use sophisticated logging.
-} -}
module GHCup.Utils.File.Posix where module GHCup.Utils.File.Posix where
import GHCup.Utils.Dirs
import GHCup.Utils.File.Common import GHCup.Utils.File.Common
import GHCup.Utils.Prelude import GHCup.Utils.Prelude
import GHCup.Utils.Logger import GHCup.Utils.Logger
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
import GHCup.Utils.File.Posix.Traversals
import Control.Concurrent import Control.Concurrent
import Control.Concurrent.Async import Control.Concurrent.Async
import qualified Control.Exception as E import Control.Exception ( evaluate )
import Control.Exception.Safe import Control.Exception.Safe
import Control.Monad import Control.Monad
import Control.Monad.Reader import Control.Monad.Reader
@@ -38,15 +34,12 @@ import Data.IORef
import Data.Sequence ( Seq, (|>) ) import Data.Sequence ( Seq, (|>) )
import Data.List import Data.List
import Data.Word8 import Data.Word8
import Foreign.C.String
import Foreign.C.Types
import GHC.IO.Exception import GHC.IO.Exception
import System.IO ( stderr, hClose, hSetBinaryMode ) import System.Console.Terminal.Common
import System.IO.Error import System.IO.Error
import System.FilePath import System.FilePath
import System.Directory
import System.Posix.Directory import System.Posix.Directory
import System.Posix.Error ( throwErrnoPathIfMinus1Retry )
import System.Posix.Internals ( withFilePath )
import System.Posix.Files import System.Posix.Files
import System.Posix.IO import System.Posix.IO
import System.Posix.Process ( ProcessStatus(..) ) import System.Posix.Process ( ProcessStatus(..) )
@@ -57,27 +50,12 @@ import qualified Control.Exception as EX
import qualified Data.Sequence as Sq import qualified Data.Sequence as Sq
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 System.Posix.Directory as PD
import qualified System.Posix.Files as PF
import qualified System.Posix.Process as SPP import qualified System.Posix.Process as SPP
import qualified System.Posix.IO as SPI import qualified System.Console.Terminal.Posix as TP
import qualified System.Console.Terminal.Size as TP
import qualified System.Posix as Posix
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import qualified "unix-bytestring" System.Posix.IO.ByteString import qualified "unix-bytestring" System.Posix.IO.ByteString
as SPIB as SPIB
import qualified Streamly.FileSystem.Handle as FH
import qualified Streamly.Internal.FileSystem.Handle
as IFH
import qualified Streamly.Prelude as S
import qualified GHCup.Utils.File.Posix.Foreign as FD
import qualified Streamly.Internal.Data.Stream.StreamD.Type
as D
import Streamly.Internal.Data.Unfold.Type
import qualified Streamly.Internal.Data.Unfold as U
import Streamly.Internal.Control.Concurrent ( withRunInIO )
import Streamly.Internal.Data.IOFinalizer ( newIOFinalizer, runIOFinalizer )
@@ -95,7 +73,6 @@ executeOut path args chdir = liftIO $ captureOutStreams $ do
execLogged :: ( MonadReader env m execLogged :: ( MonadReader env m
, HasSettings env , HasSettings env
, HasLog env
, HasDirs env , HasDirs env
, MonadIO m , MonadIO m
, MonadThrow m) , MonadThrow m)
@@ -108,8 +85,7 @@ execLogged :: ( MonadReader env m
execLogged exe args chdir lfile env = do execLogged exe args chdir lfile env = do
Settings {..} <- getSettings Settings {..} <- getSettings
Dirs {..} <- getDirs Dirs {..} <- getDirs
logDebug $ T.pack $ "Running " <> exe <> " with arguments " <> show args let logfile = logsDir </> lfile <> ".log"
let logfile = fromGHCupPath logsDir </> lfile <> ".log"
liftIO $ bracket (openFd logfile WriteOnly (Just newFilePerms) defaultFileFlags{ append = True }) liftIO $ bracket (openFd logfile WriteOnly (Just newFilePerms) defaultFileFlags{ append = True })
closeFd closeFd
(action verbose noColor) (action verbose noColor)
@@ -165,14 +141,14 @@ execLogged exe args chdir lfile env = do
printToRegion :: Fd -> Fd -> Int -> MVar Bool -> Bool -> IO () printToRegion :: Fd -> Fd -> Int -> MVar Bool -> Bool -> IO ()
printToRegion fileFd fdIn size pState no_color = do printToRegion fileFd fdIn size pState no_color = do
-- init region -- init region
forM_ [1..size] $ \_ -> BS.hPut stderr "\n" forM_ [1..size] $ \_ -> BS.putStr "\n"
void $ flip runStateT mempty void $ flip runStateT mempty
$ do $ do
handle handle
(\(ex :: SomeException) -> do (\(ex :: SomeException) -> do
ps <- liftIO $ takeMVar pState ps <- liftIO $ takeMVar pState
when ps (liftIO $ BS.hPut stderr (pos1 <> moveLineUp size <> clearScreen)) when ps (liftIO $ BS.putStr (pos1 <> moveLineUp size <> clearScreen))
throw ex throw ex
) $ readTilEOF lineAction fdIn ) $ readTilEOF lineAction fdIn
@@ -204,10 +180,10 @@ execLogged exe args chdir lfile env = do
modify (swapRegs bs') modify (swapRegs bs')
liftIO TP.size >>= \case liftIO TP.size >>= \case
Nothing -> pure () Nothing -> pure ()
Just (TP.Window _ w) -> do Just (Window _ w) -> do
regs <- get regs <- get
liftIO $ forM_ (Sq.zip regs (Sq.fromList [0..(Sq.length regs - 1)])) $ \(bs, i) -> do liftIO $ forM_ (Sq.zip regs (Sq.fromList [0..(Sq.length regs - 1)])) $ \(bs, i) -> do
BS.hPut stderr BS.putStr
. overwriteNthLine (size - i) . overwriteNthLine (size - i)
. trim w . trim w
. blue . blue
@@ -284,7 +260,7 @@ captureOutStreams action = do
-- execute the action -- execute the action
a <- action a <- action
void $ E.evaluate a void $ evaluate a
-- close everything we don't need -- close everything we don't need
closeFd childStdoutWrite closeFd childStdoutWrite
@@ -421,201 +397,3 @@ isBrokenSymlink fp = do
Right b -> pure b Right b -> pure b
Left e | isDoesNotExistError e -> pure False Left e | isDoesNotExistError e -> pure False
| otherwise -> throwIO e | otherwise -> throwIO e
copyFile :: FilePath -- ^ source file
-> FilePath -- ^ destination file
-> Bool -- ^ fail if file exists
-> IO ()
copyFile from to fail' = do
bracket
(openFdHandle from SPI.ReadOnly [FD.oNofollow] Nothing)
(hClose . snd)
$ \(fromFd, fH) -> do
sourceFileMode <- fileMode <$> getFdStatus fromFd
let dflags = [ FD.oNofollow
, if fail' then FD.oExcl else FD.oTrunc
]
bracket
(openFdHandle to SPI.WriteOnly dflags $ Just sourceFileMode)
(hClose . snd)
$ \(_, tH) -> do
hSetBinaryMode fH True
hSetBinaryMode tH True
streamlyCopy (fH, tH)
where
openFdHandle fp omode flags fM = do
fd <- openFd' fp omode flags fM
handle' <- SPI.fdToHandle fd
pure (fd, handle')
streamlyCopy (fH, tH) =
S.fold (FH.writeChunks tH) $ IFH.toChunksWithBufferOf (256 * 1024) fH
foreign import ccall unsafe "open"
c_open :: CString -> CInt -> Posix.CMode -> IO CInt
open_ :: CString
-> Posix.OpenMode
-> [FD.Flags]
-> Maybe Posix.FileMode
-> IO Posix.Fd
open_ str how optional_flags maybe_mode = do
fd <- c_open str all_flags mode_w
return (Posix.Fd fd)
where
all_flags = FD.unionFlags $ optional_flags ++ [open_mode] ++ creat
(creat, mode_w) = case maybe_mode of
Nothing -> ([],0)
Just x -> ([FD.oCreat], x)
open_mode = case how of
Posix.ReadOnly -> FD.oRdonly
Posix.WriteOnly -> FD.oWronly
Posix.ReadWrite -> FD.oRdwr
-- |Open and optionally create this file. See 'System.Posix.Files'
-- for information on how to use the 'FileMode' type.
--
-- Note that passing @Just x@ as the 4th argument triggers the
-- `oCreat` status flag, which must be set when you pass in `oExcl`
-- to the status flags. Also see the manpage for @open(2)@.
openFd' :: FilePath
-> Posix.OpenMode
-> [FD.Flags] -- ^ status flags of @open(2)@
-> Maybe Posix.FileMode -- ^ @Just x@ => creates the file with the given modes, Nothing => the file must exist.
-> IO Posix.Fd
openFd' name how optional_flags maybe_mode =
withFilePath name $ \str ->
throwErrnoPathIfMinus1Retry "openFd" name $
open_ str how optional_flags maybe_mode
-- |Deletes the given file. Raises `eISDIR`
-- if run on a directory. Does not follow symbolic links.
--
-- Throws:
--
-- - `InappropriateType` for wrong file type (directory)
-- - `NoSuchThing` if the file does not exist
-- - `PermissionDenied` if the directory cannot be read
--
-- Notes: calls `unlink`
deleteFile :: FilePath -> IO ()
deleteFile = removeLink
-- |Recreate a symlink.
--
-- In `Overwrite` copy mode only files and empty directories are deleted.
--
-- Safety/reliability concerns:
--
-- * `Overwrite` mode is inherently non-atomic
--
-- Throws:
--
-- - `InvalidArgument` if source file is wrong type (not a symlink)
-- - `PermissionDenied` if output directory cannot be written to
-- - `PermissionDenied` if source directory cannot be opened
-- - `SameFile` if source and destination are the same file
-- (`HPathIOException`)
--
--
-- Throws in `Strict` mode only:
--
-- - `AlreadyExists` if destination already exists
--
-- Throws in `Overwrite` mode only:
--
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
--
-- Notes:
--
-- - calls `symlink`
recreateSymlink :: FilePath -- ^ the old symlink file
-> FilePath -- ^ destination file
-> Bool -- ^ fail if destination file exists
-> IO ()
recreateSymlink symsource newsym fail' = do
sympoint <- readSymbolicLink symsource
case fail' of
True -> pure ()
False ->
hideError doesNotExistErrorType $ deleteFile newsym
createSymbolicLink sympoint newsym
-- copys files, recreates symlinks, fails on all other types
install :: FilePath -> FilePath -> Bool -> IO ()
install from to fail' = do
fs <- PF.getSymbolicLinkStatus from
decide fs
where
decide fs | PF.isRegularFile fs = copyFile from to fail'
| PF.isSymbolicLink fs = recreateSymlink from to fail'
| otherwise = ioError $ mkIOError illegalOperationErrorType "install: not a regular file or symlink" Nothing (Just from)
removeEmptyDirectory :: FilePath -> IO ()
removeEmptyDirectory = PD.removeDirectory
-- | Create an 'Unfold' of directory contents.
unfoldDirContents :: (MonadMask m, MonadIO m, S.MonadAsync m) => Unfold m FilePath (FD.DirType, FilePath)
unfoldDirContents = U.bracket (liftIO . openDirStream) (liftIO . closeDirStream) (Unfold step return)
where
{-# INLINE [0] step #-}
step dirstream = do
(typ, e) <- liftIO $ readDirEnt dirstream
return $ if
| null e -> D.Stop
| "." == e -> D.Skip dirstream
| ".." == e -> D.Skip dirstream
| otherwise -> D.Yield (typ, e) dirstream
getDirectoryContentsRecursiveDFSUnsafe :: (MonadMask m, MonadIO m, S.MonadAsync m)
=> FilePath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveDFSUnsafe fp = go ""
where
go cd = flip S.concatMap (S.unfold unfoldDirContents (fp </> cd)) $ \(t, f) ->
if | t == FD.dtDir -> go (cd </> f)
| otherwise -> pure (cd </> f)
getDirectoryContentsRecursiveUnfold :: (MonadMask m, MonadIO m, S.MonadAsync m) => Unfold m FilePath FilePath
getDirectoryContentsRecursiveUnfold = Unfold step (\s -> return (s, Nothing, [""]))
where
{-# INLINE [0] step #-}
step (_, Nothing, []) = return D.Stop
step (topdir, Just (cdir, dirstream, finalizer), dirs) = flip onException (runIOFinalizer finalizer) $ do
(dt, f) <- liftIO $ readDirEnt dirstream
if | FD.dtUnknown == dt -> do
runIOFinalizer finalizer
return $ D.Skip (topdir, Nothing, dirs)
| f == "." || f == ".."
-> return $ D.Skip (topdir, Just (cdir, dirstream, finalizer), dirs)
| FD.dtDir == dt -> return $ D.Skip (topdir, Just (cdir, dirstream, finalizer), (cdir </> f):dirs)
| otherwise -> return $ D.Yield (cdir </> f) (topdir, Just (cdir, dirstream, finalizer), dirs)
step (topdir, Nothing, dir:dirs) = do
(s, f) <- acquire (topdir </> dir)
return $ D.Skip (topdir, Just (dir, s, f), dirs)
acquire dir =
withRunInIO $ \run -> mask_ $ run $ do
dirstream <- liftIO $ openDirStream dir
ref <- newIOFinalizer (liftIO $ closeDirStream dirstream)
return (dirstream, ref)
getDirectoryContentsRecursiveBFSUnsafe :: (MonadMask m, MonadIO m, S.MonadAsync m)
=> FilePath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveBFSUnsafe = S.unfold getDirectoryContentsRecursiveUnfold

View File

@@ -1,58 +0,0 @@
{-# LANGUAGE PatternSynonyms #-}
module GHCup.Utils.File.Posix.Foreign where
import Data.Bits
import Data.List (foldl')
import Foreign.C.Types
#include <limits.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
newtype DirType = DirType Int deriving (Eq, Show)
data Flags = Flags Int | UnsupportedFlag String deriving (Eq, Show)
unFlags :: Flags -> Int
unFlags (Flags i) = i
unFlags (UnsupportedFlag name) = error (name ++ " is not supported on this platform")
-- |Returns @True@ if posix-paths was compiled with support for the provided
-- flag. (As of this writing, the only flag for which this check may be
-- necessary is 'oCloexec'; all other flags will always yield @True@.)
isSupported :: Flags -> Bool
isSupported (Flags _) = True
isSupported _ = False
-- |@O_CLOEXEC@ is not supported on every POSIX platform. Use
-- @'isSupported' oCloexec@ to determine if support for @O_CLOEXEC@ was
-- compiled into your version of posix-paths. (If not, using @oCloexec@ will
-- throw an exception.)
oCloexec :: Flags
#ifdef O_CLOEXEC
oCloexec = Flags #{const O_CLOEXEC}
#else
{-# WARNING oCloexec
"This version of posix-paths was compiled without @O_CLOEXEC@ support." #-}
oCloexec = UnsupportedFlag "O_CLOEXEC"
#endif
-- If these enum declarations occur earlier in the file, haddock
-- gets royally confused about the above doc comments.
-- Probably http://trac.haskell.org/haddock/ticket/138
#{enum DirType, DirType, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN}
#{enum Flags, Flags, O_APPEND, O_ASYNC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_WRONLY, O_RDWR, O_SYNC, O_TRUNC}
pathMax :: Int
pathMax = #{const PATH_MAX}
unionFlags :: [Flags] -> CInt
unionFlags = fromIntegral . foldl' ((. unFlags) . (.|.)) 0

View File

@@ -1,92 +0,0 @@
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wall #-}
module GHCup.Utils.File.Posix.Traversals (
-- lower-level stuff
readDirEnt
, unpackDirStream
) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
#endif
import GHCup.Utils.File.Posix.Foreign
import Unsafe.Coerce (unsafeCoerce)
import Foreign.C.Error
import Foreign.C.String
import Foreign.C.Types
import Foreign.Ptr
import Foreign.Storable
import System.Posix
import Foreign (alloca)
import System.Posix.Internals (peekFilePath)
----------------------------------------------------------
-- dodgy stuff
type CDir = ()
type CDirent = ()
-- Posix doesn't export DirStream, so to re-use that type we need to use
-- unsafeCoerce. It's just a newtype, so this is a legitimate usage.
-- ugly trick.
unpackDirStream :: DirStream -> Ptr CDir
unpackDirStream = unsafeCoerce
-- the __hscore_* functions are defined in the unix package. We can import them and let
-- the linker figure it out.
foreign import ccall unsafe "__hscore_readdir"
c_readdir :: Ptr CDir -> Ptr (Ptr CDirent) -> IO CInt
foreign import ccall unsafe "__hscore_free_dirent"
c_freeDirEnt :: Ptr CDirent -> IO ()
foreign import ccall unsafe "__hscore_d_name"
c_name :: Ptr CDirent -> IO CString
foreign import ccall unsafe "__posixdir_d_type"
c_type :: Ptr CDirent -> IO DirType
----------------------------------------------------------
-- less dodgy but still lower-level
readDirEnt :: DirStream -> IO (DirType, FilePath)
readDirEnt (unpackDirStream -> dirp) =
alloca $ \ptr_dEnt -> loop ptr_dEnt
where
loop ptr_dEnt = do
resetErrno
r <- c_readdir dirp ptr_dEnt
if r == 0
then do
dEnt <- peek ptr_dEnt
if dEnt == nullPtr
then return (dtUnknown, mempty)
else do
dName <- c_name dEnt >>= peekFilePath
dType <- c_type dEnt
c_freeDirEnt dEnt
return (dType, dName)
else do
errno <- getErrno
if errno == eINTR
then loop ptr_dEnt
else do
let (Errno eo) = errno
if eo == 0
then return (dtUnknown, mempty)
else throwErrno "readDirEnt"

View File

@@ -1,6 +1,5 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-| {-|
Module : GHCup.Utils.File.Windows Module : GHCup.Utils.File.Windows
@@ -19,7 +18,6 @@ module GHCup.Utils.File.Windows where
import {-# SOURCE #-} GHCup.Utils ( getLinkTarget, pathIsLink ) import {-# SOURCE #-} GHCup.Utils ( getLinkTarget, pathIsLink )
import GHCup.Utils.Dirs import GHCup.Utils.Dirs
import GHCup.Utils.File.Common import GHCup.Utils.File.Common
import GHCup.Utils.Logger
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
@@ -32,29 +30,16 @@ import Data.List
import Foreign.C.Error import Foreign.C.Error
import GHC.IO.Exception import GHC.IO.Exception
import GHC.IO.Handle import GHC.IO.Handle
import qualified GHC.Unicode as U import System.Directory
import System.Environment import System.Environment
import System.FilePath import System.FilePath
import System.IO import System.IO
import qualified System.IO.Error as IOE
import System.Process import System.Process
import qualified System.Win32.Info as WS
import qualified System.Win32.File as WS
import qualified Control.Exception as EX import qualified Control.Exception as EX
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL 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 Streamly.Internal.Data.Stream.StreamD.Type
as D
import Streamly.Internal.Data.Unfold.Type hiding ( concatMap )
import Data.Bits ((.&.))
import qualified Streamly.Prelude as S
import qualified Streamly.Internal.Data.Unfold as U
import Streamly.Internal.Control.Concurrent ( withRunInIO )
import Streamly.Internal.Data.IOFinalizer ( newIOFinalizer, runIOFinalizer )
@@ -164,7 +149,6 @@ executeOut path args chdir = do
execLogged :: ( MonadReader env m execLogged :: ( MonadReader env m
, HasDirs env , HasDirs env
, HasLog env
, HasSettings env , HasSettings env
, MonadIO m , MonadIO m
, MonadThrow m) , MonadThrow m)
@@ -176,9 +160,8 @@ execLogged :: ( MonadReader env m
-> m (Either ProcessError ()) -> m (Either ProcessError ())
execLogged exe args chdir lfile env = do execLogged exe args chdir lfile env = do
Dirs {..} <- getDirs Dirs {..} <- getDirs
logDebug $ T.pack $ "Running " <> exe <> " with arguments " <> show args let stdoutLogfile = logsDir </> lfile <> ".stdout.log"
let stdoutLogfile = fromGHCupPath logsDir </> lfile <> ".stdout.log" stderrLogfile = logsDir </> lfile <> ".stderr.log"
stderrLogfile = fromGHCupPath logsDir </> lfile <> ".stderr.log"
cp <- createProcessWithMingwPath ((proc exe args) cp <- createProcessWithMingwPath ((proc exe args)
{ cwd = chdir { cwd = chdir
, env = env , env = env
@@ -209,8 +192,7 @@ execLogged exe args chdir lfile env = do
then pure () then pure ()
else do else do
void $ BS.appendFile logFile some void $ BS.appendFile logFile some
-- subprocess stdout also goes to stderr for logging void $ BS.hPut stdout some
void $ BS.hPut stderr some
go go
@@ -269,7 +251,7 @@ ghcupMsys2Dir =
Just fp -> pure fp Just fp -> pure fp
Nothing -> do Nothing -> do
baseDir <- liftIO ghcupBaseDir baseDir <- liftIO ghcupBaseDir
pure (fromGHCupPath baseDir </> "msys64") pure (baseDir </> "msys64")
-- | Checks whether the binary is a broken link. -- | Checks whether the binary is a broken link.
isBrokenSymlink :: FilePath -> IO Bool isBrokenSymlink :: FilePath -> IO Bool
@@ -282,229 +264,3 @@ isBrokenSymlink fp = do
-- this drops 'symDir' if 'tfp' is absolute -- this drops 'symDir' if 'tfp' is absolute
(takeDirectory fp </> tfp) (takeDirectory fp </> tfp)
else pure False else pure False
copyFile :: FilePath -- ^ source file
-> FilePath -- ^ destination file
-> Bool -- ^ fail if file exists
-> IO ()
copyFile = WS.copyFile
deleteFile :: FilePath -> IO ()
deleteFile = WS.deleteFile
install :: FilePath -> FilePath -> Bool -> IO ()
install = copyFile
removeEmptyDirectory :: FilePath -> IO ()
removeEmptyDirectory = WS.removeDirectory
unfoldDirContents :: (S.MonadAsync m, MonadIO m, MonadCatch m, MonadMask m) => Unfold m FilePath (WS.FileAttributeOrFlag, FilePath)
unfoldDirContents = U.bracket alloc dealloc (Unfold step return)
where
{-# INLINE [0] step #-}
step (_, False, _, _) = return D.Stop
step (topdir, True, h, fd) = flip onException (liftIO $ WS.findClose h) $ do
f <- liftIO $ WS.getFindDataFileName fd
more <- liftIO $ WS.findNextFile h fd
-- can't get file attribute from FindData yet (needs Win32 PR)
fattr <- liftIO $ WS.getFileAttributes (topdir </> f)
if | f == "." || f == ".." -> return $ D.Skip (topdir, more, h, fd)
| otherwise -> return $ D.Yield (fattr, f) (topdir, more, h, fd)
alloc topdir = do
query <- liftIO $ furnishPath (topdir </> "*")
(h, fd) <- liftIO $ WS.findFirstFile query
pure (topdir, True, h, fd)
dealloc (_, _, fd, _) = liftIO $ WS.findClose fd
getDirectoryContentsRecursiveDFSUnsafe :: (MonadCatch m, S.MonadAsync m, MonadMask m, S.IsStream t)
=> FilePath
-> t m FilePath
getDirectoryContentsRecursiveDFSUnsafe fp = go ""
where
isDir attrs = attrs .&. WS.fILE_ATTRIBUTE_DIRECTORY /= 0
go cd = flip S.concatMap (S.unfold unfoldDirContents (fp </> cd)) $ \(t, f) ->
if | isDir t -> go (cd </> f)
| otherwise -> pure (cd </> f)
getDirectoryContentsRecursiveUnfold :: (MonadCatch m, S.MonadAsync m, MonadMask m) => Unfold m FilePath FilePath
getDirectoryContentsRecursiveUnfold = Unfold step init'
where
{-# INLINE [0] step #-}
step (_, Nothing, []) = return D.Stop
step (topdir, state@(Just (cdir, (h, findData, ref))), dirs) = flip onException (runIOFinalizer ref) $ do
f <- liftIO $ WS.getFindDataFileName findData
more <- liftIO $ WS.findNextFile h findData
when (not more) $ runIOFinalizer ref
let nextState = if more then state else Nothing
-- can't get file attribute from FindData yet (needs Win32 PR)
fattr <- liftIO $ WS.getFileAttributes (topdir </> cdir </> f)
if | f == "." || f == ".." -> return $ D.Skip (topdir, nextState, dirs)
| isDir fattr -> return $ D.Skip (topdir, nextState, (cdir </> f):dirs)
| otherwise -> return $ D.Yield (cdir </> f) (topdir, nextState, dirs)
step (topdir, Nothing, dir:dirs) = do
(h, findData, ref) <- acquire (topdir </> dir)
return $ D.Skip (topdir, Just (dir, (h, findData, ref)), dirs)
init' topdir = do
(h, findData, ref) <- acquire topdir
return (topdir, Just ("", (h, findData, ref)), [])
isDir attrs = attrs .&. WS.fILE_ATTRIBUTE_DIRECTORY /= 0
acquire dir = do
query <- liftIO $ furnishPath (dir </> "*")
withRunInIO $ \run -> mask_ $ run $ do
(h, findData) <- liftIO $ WS.findFirstFile query
ref <- newIOFinalizer (liftIO $ WS.findClose h)
return (h, findData, ref)
getDirectoryContentsRecursiveBFSUnsafe :: (MonadMask m, MonadIO m, S.MonadAsync m)
=> FilePath
-> S.SerialT m FilePath
getDirectoryContentsRecursiveBFSUnsafe = S.unfold getDirectoryContentsRecursiveUnfold
--------------------------------------
--[ Inlined from directory package ]--
--------------------------------------
furnishPath :: FilePath -> IO FilePath
furnishPath path =
(toExtendedLengthPath <$> rawPrependCurrentDirectory path)
`IOE.catchIOError` \ _ ->
pure path
toExtendedLengthPath :: FilePath -> FilePath
toExtendedLengthPath path
| isRelative path = simplifiedPath
| otherwise =
case simplifiedPath of
'\\' : '?' : '?' : '\\' : _ -> simplifiedPath
'\\' : '\\' : '?' : '\\' : _ -> simplifiedPath
'\\' : '\\' : '.' : '\\' : _ -> simplifiedPath
'\\' : subpath@('\\' : _) -> "\\\\?\\UNC" <> subpath
_ -> "\\\\?\\" <> simplifiedPath
where simplifiedPath = simplify path
simplify :: FilePath -> FilePath
simplify = simplifyWindows
simplifyWindows :: FilePath -> FilePath
simplifyWindows "" = ""
simplifyWindows path =
case drive' of
"\\\\?\\" -> drive' <> subpath
_ -> simplifiedPath
where
simplifiedPath = joinDrive drive' subpath'
(drive, subpath) = splitDrive path
drive' = upperDrive (normaliseTrailingSep (normalisePathSeps drive))
subpath' = appendSep . avoidEmpty . prependSep . joinPath .
stripPardirs . expandDots . skipSeps .
splitDirectories $ subpath
upperDrive d = case d of
c : ':' : s | U.isAlpha c && all isPathSeparator s -> U.toUpper c : ':' : s
_ -> d
skipSeps = filter (not . (`elem` (pure <$> pathSeparators)))
stripPardirs | pathIsAbsolute || subpathIsAbsolute = dropWhile (== "..")
| otherwise = id
prependSep | subpathIsAbsolute = (pathSeparator :)
| otherwise = id
avoidEmpty | not pathIsAbsolute
&& (null drive || hasTrailingPathSep) -- prefer "C:" over "C:."
= emptyToCurDir
| otherwise = id
appendSep p | hasTrailingPathSep
&& not (pathIsAbsolute && null p)
= addTrailingPathSeparator p
| otherwise = p
pathIsAbsolute = not (isRelative path)
subpathIsAbsolute = any isPathSeparator (take 1 subpath)
hasTrailingPathSep = hasTrailingPathSeparator subpath
emptyToCurDir :: FilePath -> FilePath
emptyToCurDir "" = "."
emptyToCurDir path = path
normaliseTrailingSep :: FilePath -> FilePath
normaliseTrailingSep path = do
let path' = reverse path
let (sep, path'') = span isPathSeparator path'
let addSep = if null sep then id else (pathSeparator :)
reverse (addSep path'')
normalisePathSeps :: FilePath -> FilePath
normalisePathSeps p = (\ c -> if isPathSeparator c then pathSeparator else c) <$> p
expandDots :: [FilePath] -> [FilePath]
expandDots = reverse . go []
where
go ys' xs' =
case xs' of
[] -> ys'
x : xs ->
case x of
"." -> go ys' xs
".." ->
case ys' of
[] -> go (x : ys') xs
".." : _ -> go (x : ys') xs
_ : ys -> go ys xs
_ -> go (x : ys') xs
rawPrependCurrentDirectory :: FilePath -> IO FilePath
rawPrependCurrentDirectory path
| isRelative path =
((`ioeAddLocation` "prependCurrentDirectory") .
(`IOE.ioeSetFileName` path)) `IOE.modifyIOError` do
getFullPathName path
| otherwise = pure path
ioeAddLocation :: IOError -> String -> IOError
ioeAddLocation e loc = do
IOE.ioeSetLocation e newLoc
where
newLoc = loc <> if null oldLoc then "" else ":" <> oldLoc
oldLoc = IOE.ioeGetLocation e
getFullPathName :: FilePath -> IO FilePath
getFullPathName path =
fromExtendedLengthPath <$> WS.getFullPathName (toExtendedLengthPath path)
fromExtendedLengthPath :: FilePath -> FilePath
fromExtendedLengthPath ePath =
case ePath of
'\\' : '\\' : '?' : '\\' : path ->
case path of
'U' : 'N' : 'C' : subpath@('\\' : _) -> "\\" <> subpath
drive : ':' : subpath
-- if the path is not "regular", then the prefix is necessary
-- to ensure the path is interpreted literally
| U.isAlpha drive && U.isAscii drive && isPathRegular subpath -> path
_ -> ePath
_ -> ePath
where
isPathRegular path =
not ('/' `elem` path ||
"." `elem` splitDirectories path ||
".." `elem` splitDirectories path)

View File

@@ -1,4 +1,5 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
@@ -17,8 +18,7 @@ module GHCup.Utils.Logger where
import GHCup.Types import GHCup.Types
import GHCup.Types.Optics import GHCup.Types.Optics
import {-# SOURCE #-} GHCup.Utils.Dirs (fromGHCupPath) import {-# SOURCE #-} GHCup.Utils.File.Common
import {-# SOURCE #-} GHCup.Utils.File.Common (findFiles)
import GHCup.Utils.String.QQ import GHCup.Utils.String.QQ
import Control.Exception.Safe import Control.Exception.Safe
@@ -118,14 +118,14 @@ initGHCupFileLogging :: ( MonadReader env m
) => m FilePath ) => m FilePath
initGHCupFileLogging = do initGHCupFileLogging = do
Dirs { logsDir } <- getDirs Dirs { logsDir } <- getDirs
let logfile = fromGHCupPath logsDir </> "ghcup.log" let logfile = logsDir </> "ghcup.log"
logFiles <- liftIO $ findFiles logFiles <- liftIO $ findFiles
(fromGHCupPath logsDir) logsDir
(makeRegexOpts compExtended (makeRegexOpts compExtended
execBlank execBlank
([s|^.*\.log$|] :: B.ByteString) ([s|^.*\.log$|] :: B.ByteString)
) )
forM_ logFiles $ hideError doesNotExistErrorType . recycleFile . (fromGHCupPath logsDir </>) forM_ logFiles $ hideError doesNotExistErrorType . recycleFile . (logsDir </>)
liftIO $ writeFile logfile "" liftIO $ writeFile logfile ""
pure logfile pure logfile

View File

@@ -1,5 +1,7 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
module GHCup.Utils.Logger where module GHCup.Utils.Logger where

View File

@@ -1,14 +0,0 @@
module GHCup.Utils.Posix where
-- | Enables ANSI support on windows, does nothing on unix.
--
-- Returns 'Left str' on errors and 'Right bool' on success, where
-- 'bool' markes whether ansi support was already enabled.
--
-- This function never crashes.
--
-- Rip-off of https://docs.rs/ansi_term/0.12.1/x86_64-pc-windows-msvc/src/ansi_term/windows.rs.html#10-61
enableAnsiSupport :: IO (Either String Bool)
enableAnsiSupport = pure (Right True)

View File

@@ -17,26 +17,14 @@ Portability : portable
GHCup specific prelude. Lots of Excepts functionality. GHCup specific prelude. Lots of Excepts functionality.
-} -}
module GHCup.Utils.Prelude module GHCup.Utils.Prelude where
(module GHCup.Utils.Prelude,
#if defined(IS_WINDOWS)
module GHCup.Utils.Prelude.Windows
#else
module GHCup.Utils.Prelude.Posix
#endif
)
where
import {-# SOURCE #-} GHCup.Utils.Dirs (GHCupPath, fromGHCupPath, createTempGHCupDirectory, appendGHCupPath, removePathForcibly, removeDirectory) #if defined(IS_WINDOWS)
import GHCup.Types import GHCup.Types
#endif
import GHCup.Errors import GHCup.Errors
import GHCup.Types.Optics import GHCup.Types.Optics
import {-# SOURCE #-} GHCup.Utils.Logger (logWarn) import {-# SOURCE #-} GHCup.Utils.Logger
#if defined(IS_WINDOWS)
import GHCup.Utils.Prelude.Windows
#else
import GHCup.Utils.Prelude.Posix
#endif
import Control.Applicative import Control.Applicative
import Control.Exception.Safe import Control.Exception.Safe
@@ -45,8 +33,9 @@ import Control.Monad.IO.Class
import Control.Monad.Reader import Control.Monad.Reader
import Data.Bifunctor import Data.Bifunctor
import Data.ByteString ( ByteString ) import Data.ByteString ( ByteString )
import Data.List ( intercalate, stripPrefix, isPrefixOf, dropWhileEnd, intersperse ) import Data.List ( nub, intercalate, stripPrefix, isPrefixOf, dropWhileEnd )
import Data.Maybe import Data.Maybe
import Data.Foldable
import Data.List.NonEmpty ( NonEmpty( (:|) )) import Data.List.NonEmpty ( NonEmpty( (:|) ))
import Data.String import Data.String
import Data.Text ( Text ) import Data.Text ( Text )
@@ -56,15 +45,17 @@ import Haskus.Utils.Types.List
import Haskus.Utils.Variant.Excepts import Haskus.Utils.Variant.Excepts
import Text.PrettyPrint.HughesPJClass ( prettyShow, Pretty ) import Text.PrettyPrint.HughesPJClass ( prettyShow, Pretty )
import System.IO.Error import System.IO.Error
import System.Directory hiding ( removeDirectory #if defined(IS_WINDOWS)
, removeDirectoryRecursive import System.IO.Temp
, removePathForcibly #endif
, copyFile import System.IO.Unsafe
) import System.Directory
import System.FilePath import System.FilePath
#if defined(IS_WINDOWS)
import Control.Retry import Control.Retry
import GHC.IO.Exception import GHC.IO.Exception
#endif
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
@@ -78,7 +69,9 @@ 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
import qualified Data.Text.Lazy.Encoding as TLE import qualified Data.Text.Lazy.Encoding as TLE
#if defined(IS_WINDOWS)
import qualified System.Win32.File as Win32
#endif
-- $setup -- $setup
@@ -311,46 +304,23 @@ intToText :: Integral a => a -> T.Text
intToText = TL.toStrict . B.toLazyText . B.decimal intToText = TL.toStrict . B.toLazyText . B.decimal
pvpToVersion :: MonadThrow m => PVP -> Text -> m Version removeLensFieldLabel :: String -> String
pvpToVersion pvp_ rest = removeLensFieldLabel str' =
either (\_ -> throwM $ ParseError "Couldn't convert PVP to Version") pure . version . (<> rest) . prettyPVP $ pvp_ maybe str' T.unpack . T.stripPrefix (T.pack "_") . T.pack $ str'
-- | Convert a version to a PVP and unparsable rest.
-- pvpToVersion :: MonadThrow m => PVP -> m Version
-- -- prop> \v -> let (Just (pvp', r)) = versionToPVP v in pvpToVersion pvp' r === Just v pvpToVersion =
versionToPVP :: MonadThrow m => Version -> m (PVP, Text) either (\_ -> throwM $ ParseError "Couldn't convert PVP to Version") pure . version . prettyPVP
versionToPVP (Version (Just _) _ _ _) = throwM $ ParseError "Unexpected epoch"
versionToPVP v = either (\_ -> (, rest v) <$> alternative v) (pure . (, mempty)) . pvp . prettyVer $ v versionToPVP :: MonadThrow m => Version -> m PVP
versionToPVP v = either (\_ -> alternative v) pure . pvp . prettyVer $ v
where where
alternative :: MonadThrow m => Version -> m PVP alternative :: MonadThrow m => Version -> m PVP
alternative v' = case NE.takeWhile isDigit (_vChunks v') of alternative v' = case NE.takeWhile isDigit (_vChunks v') of
[] -> throwM $ ParseError "Couldn't convert Version to PVP" [] -> throwM $ ParseError "Couldn't convert Version to PVP"
xs -> pure $ pvpFromList (unsafeDigit <$> xs) xs -> pure $ pvpFromList (unsafeDigit <$> xs)
rest :: Version -> Text
rest (Version _ cs pr me) =
let chunks = NE.dropWhile isDigit cs
ver = intersperse (T.pack ".") . chunksAsT $ chunks
me' = maybe [] (\m -> [T.pack "+",m]) me
pr' = foldable [] (T.pack "-" :) $ intersperse (T.pack ".") (chunksAsT pr)
prefix = case (ver, pr', me') of
(_:_, _, _) -> T.pack "."
_ -> T.pack ""
in prefix <> mconcat (ver <> pr' <> me')
where
chunksAsT :: Functor t => t VChunk -> t Text
chunksAsT = fmap (foldMap f)
where
f :: VUnit -> Text
f (Digits i) = T.pack $ show i
f (Str s) = s
foldable :: Foldable f => f b -> (f a -> f b) -> f a -> f b
foldable d g f | null f = d
| otherwise = g f
isDigit :: VChunk -> Bool isDigit :: VChunk -> Bool
isDigit (Digits _ :| []) = True isDigit (Digits _ :| []) = True
isDigit _ = False isDigit _ = False
@@ -400,6 +370,64 @@ createDirRecursive' p =
_ -> throwIO e _ -> throwIO e
-- | Recursively copy the contents of one directory to another path.
--
-- This is a rip-off of Cabal library.
copyDirectoryRecursive :: FilePath -> FilePath -> (FilePath -> FilePath -> IO ()) -> IO ()
copyDirectoryRecursive srcDir destDir doCopy = do
srcFiles <- getDirectoryContentsRecursive srcDir
copyFilesWith destDir [ (srcDir, f)
| f <- srcFiles ]
where
-- | Common implementation of 'copyFiles', 'installOrdinaryFiles',
-- 'installExecutableFiles' and 'installMaybeExecutableFiles'.
copyFilesWith :: FilePath -> [(FilePath, FilePath)] -> IO ()
copyFilesWith targetDir srcFiles = do
-- Create parent directories for everything
let dirs = map (targetDir </>) . nub . map (takeDirectory . snd) $ srcFiles
traverse_ (createDirectoryIfMissing True) dirs
-- Copy all the files
sequence_ [ let src = srcBase </> srcFile
dest = targetDir </> srcFile
in doCopy src dest
| (srcBase, srcFile) <- srcFiles ]
-- | List all the files in a directory and all subdirectories.
--
-- The order places files in sub-directories after all the files in their
-- parent directories. The list is generated lazily so is not well defined if
-- the source directory structure changes before the list is used.
--
getDirectoryContentsRecursive :: FilePath -> IO [FilePath]
getDirectoryContentsRecursive topdir = recurseDirectories [""]
where
recurseDirectories :: [FilePath] -> IO [FilePath]
recurseDirectories [] = return []
recurseDirectories (dir:dirs) = unsafeInterleaveIO $ do
(files, dirs') <- collect [] [] =<< getDirectoryContents (topdir </> dir)
files' <- recurseDirectories (dirs' ++ dirs)
return (files ++ files')
where
collect files dirs' [] = return (reverse files
,reverse dirs')
collect files dirs' (entry:entries) | ignore entry
= collect files dirs' entries
collect files dirs' (entry:entries) = do
let dirEntry = dir </> entry
isDirectory <- doesDirectoryExist (topdir </> dirEntry)
if isDirectory
then collect files (dirEntry:dirs') entries
else collect (dirEntry:files) dirs' entries
ignore ['.'] = True
ignore ['.', '.'] = True
ignore _ = False
-- https://github.com/haskell/directory/issues/110 -- https://github.com/haskell/directory/issues/110
-- https://github.com/haskell/directory/issues/96 -- https://github.com/haskell/directory/issues/96
-- https://www.sqlite.org/src/info/89f1848d7f -- https://www.sqlite.org/src/info/89f1848d7f
@@ -408,39 +436,45 @@ recyclePathForcibly :: ( MonadIO m
, HasDirs env , HasDirs env
, MonadMask m , MonadMask m
) )
=> GHCupPath => FilePath
-> m () -> m ()
recyclePathForcibly fp recyclePathForcibly fp = do
| isWindows = do #if defined(IS_WINDOWS)
Dirs { recycleDir } <- getDirs Dirs { recycleDir } <- getDirs
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recyclePathForcibly" tmp <- liftIO $ createTempDirectory recycleDir "recyclePathForcibly"
let dest = tmp `appendGHCupPath` takeFileName (fromGHCupPath fp) let dest = tmp </> takeFileName fp
liftIO (moveFile (fromGHCupPath fp) (fromGHCupPath dest)) liftIO (Win32.moveFileEx fp (Just dest) 0)
`catch` `catch`
(\e -> if | isDoesNotExistError e -> pure () (\e -> if isPermissionError e {- EXDEV on windows -} then recover (liftIO $ removePathForcibly fp) else throwIO e)
| isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} -> recover (liftIO $ removePathForcibly fp) `finally`
| otherwise -> throwIO e) (liftIO $ handleIO (\_ -> pure ()) $ removePathForcibly tmp)
`finally` #else
liftIO (handleIO (\_ -> pure ()) $ removePathForcibly tmp) liftIO $ removePathForcibly fp
| otherwise = liftIO $ removePathForcibly fp #endif
rmPathForcibly :: ( MonadIO m rmPathForcibly :: ( MonadIO m
, MonadMask m , MonadMask m
) )
=> GHCupPath => FilePath
-> m () -> m ()
rmPathForcibly fp rmPathForcibly fp =
| isWindows = recover (liftIO $ removePathForcibly fp) #if defined(IS_WINDOWS)
| otherwise = liftIO $ removePathForcibly fp recover (liftIO $ removePathForcibly fp)
#else
liftIO $ removePathForcibly fp
#endif
rmDirectory :: (MonadIO m, MonadMask m) rmDirectory :: (MonadIO m, MonadMask m)
=> GHCupPath => FilePath
-> m () -> m ()
rmDirectory fp rmDirectory fp =
| isWindows = recover (liftIO $ removeDirectory fp) #if defined(IS_WINDOWS)
| otherwise = liftIO $ removeDirectory fp recover (liftIO $ removeDirectory fp)
#else
liftIO $ removeDirectory fp
#endif
-- https://www.sqlite.org/src/info/89f1848d7f -- https://www.sqlite.org/src/info/89f1848d7f
@@ -452,18 +486,20 @@ recycleFile :: ( MonadIO m
) )
=> FilePath => FilePath
-> m () -> m ()
recycleFile fp recycleFile fp = do
| isWindows = do #if defined(IS_WINDOWS)
Dirs { recycleDir } <- getDirs Dirs { recycleDir } <- getDirs
liftIO $ whenM (doesDirectoryExist fp) $ ioError (IOError Nothing InappropriateType "recycleFile" "" Nothing (Just fp)) liftIO $ whenM (doesDirectoryExist fp) $ ioError (IOError Nothing InappropriateType "recycleFile" "" Nothing (Just fp))
tmp <- liftIO $ createTempGHCupDirectory recycleDir "recycleFile" tmp <- liftIO $ createTempDirectory recycleDir "recycleFile"
let dest = fromGHCupPath tmp </> takeFileName fp let dest = tmp </> takeFileName fp
liftIO (moveFile fp dest) liftIO (Win32.moveFileEx fp (Just dest) 0)
`catch` `catch`
(\e -> if isPermissionError e || ioeGetErrorType e == UnsupportedOperation {- EXDEV on windows -} then recover (liftIO $ rmFile fp) else throwIO e) (\e -> if isPermissionError e {- EXDEV on windows -} then recover (liftIO $ removePathForcibly fp) else throwIO e)
`finally` `finally`
liftIO (handleIO (\_ -> pure ()) $ removePathForcibly tmp) (liftIO $ handleIO (\_ -> pure ()) $ removePathForcibly tmp)
| otherwise = liftIO $ removeFile fp #else
liftIO $ removeFile fp
#endif
rmFile :: ( MonadIO m rmFile :: ( MonadIO m
@@ -471,19 +507,26 @@ rmFile :: ( MonadIO m
) )
=> FilePath => FilePath
-> m () -> m ()
rmFile fp rmFile fp =
| isWindows = recover (liftIO $ removeFile fp) #if defined(IS_WINDOWS)
| otherwise = liftIO $ removeFile fp recover (liftIO $ removeFile fp)
#else
liftIO $ removeFile fp
#endif
rmDirectoryLink :: (MonadIO m, MonadMask m, MonadReader env m, HasDirs env) rmDirectoryLink :: (MonadIO m, MonadMask m, MonadReader env m, HasDirs env)
=> FilePath => FilePath
-> m () -> m ()
rmDirectoryLink fp rmDirectoryLink fp =
| isWindows = recover (liftIO $ removeDirectoryLink fp) #if defined(IS_WINDOWS)
| otherwise = liftIO $ removeDirectoryLink fp recover (liftIO $ removeDirectoryLink fp)
#else
liftIO $ removeDirectoryLink fp
#endif
#if defined(IS_WINDOWS)
recover :: (MonadIO m, MonadMask m) => m a -> m a recover :: (MonadIO m, MonadMask m) => m a -> m a
recover action = recover action =
recovering (fullJitterBackoff 25000 <> limitRetries 10) recovering (fullJitterBackoff 25000 <> limitRetries 10)
@@ -492,6 +535,11 @@ recover action =
,\_ -> Handler (\e -> pure (ioeGetErrorType e == UnsatisfiedConstraints)) ,\_ -> Handler (\e -> pure (ioeGetErrorType e == UnsatisfiedConstraints))
] ]
(\_ -> action) (\_ -> action)
#endif
copyFileE :: (CopyError :< xs, MonadCatch m, MonadIO m) => FilePath -> FilePath -> Excepts xs m ()
copyFileE from = handleIO (throwE . CopyError . show) . liftIO . copyFile from
-- | Gathering monoidal values -- | Gathering monoidal values
@@ -703,3 +751,6 @@ breakOn :: Eq a => [a] -> [a] -> ([a], [a])
breakOn needle haystack | needle `isPrefixOf` haystack = ([], haystack) breakOn needle haystack | needle `isPrefixOf` haystack = ([], haystack)
breakOn _ [] = ([], []) breakOn _ [] = ([], [])
breakOn needle (x:xs) = first (x:) $ breakOn needle xs breakOn needle (x:xs) = first (x:) $ breakOn needle xs

View File

@@ -1,24 +0,0 @@
module GHCup.Utils.Prelude.Posix where
import System.Directory hiding ( removeDirectory
, removeDirectoryRecursive
, removePathForcibly
, findFiles
)
import System.Posix.Files
isWindows, isNotWindows :: Bool
isWindows = False
isNotWindows = not isWindows
moveFile :: FilePath -> FilePath -> IO ()
moveFile = rename
moveFilePortable :: FilePath -> FilePath -> IO ()
moveFilePortable from to = do
copyFile from to
removeFile from

View File

@@ -1,17 +0,0 @@
module GHCup.Utils.Prelude.Windows where
import qualified System.Win32.File as Win32
isWindows, isNotWindows :: Bool
isWindows = True
isNotWindows = not isWindows
moveFile :: FilePath -> FilePath -> IO ()
moveFile from to = Win32.moveFileEx from (Just to) 0
moveFilePortable :: FilePath -> FilePath -> IO ()
moveFilePortable = Win32.moveFile

View File

@@ -1,4 +1,4 @@
{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TemplateHaskell #-}
{-| {-|
Module : GHCup.Utils.String.QQ Module : GHCup.Utils.String.QQ

View File

@@ -4,7 +4,7 @@
{-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveLift #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE TemplateHaskell #-}
{-| {-|
@@ -53,9 +53,6 @@ deriving instance Data VUnit
#if !MIN_VERSION_base(4,13,0) #if !MIN_VERSION_base(4,13,0)
deriving instance Lift (NonEmpty Word) deriving instance Lift (NonEmpty Word)
deriving instance Lift (NonEmpty VChunk)
deriving instance Lift (NonEmpty MChunk)
deriving instance Lift (NonEmpty VUnit)
#endif #endif
qq :: (Text -> Q Exp) -> QuasiQuoter qq :: (Text -> Q Exp) -> QuasiQuoter

View File

@@ -1,48 +0,0 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module GHCup.Utils.Windows where
import Control.Exception.Safe
import Control.Monad
#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
import Data.Bits
import System.Win32.Console
import System.Win32.File hiding ( copyFile )
import System.Win32.Types
-- | Enables ANSI support on windows, does nothing on unix.
--
-- Returns 'Left str' on errors and 'Right bool' on success, where
-- 'bool' markes whether ansi support was already enabled.
--
-- This function never crashes.
--
-- Rip-off of https://docs.rs/ansi_term/0.12.1/x86_64-pc-windows-msvc/src/ansi_term/windows.rs.html#10-61
enableAnsiSupport :: IO (Either String Bool)
enableAnsiSupport = handleIO (pure . Left . displayException) $ do
-- ref: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
-- Using `CreateFileW("CONOUT$", ...)` to retrieve the console handle works correctly even if STDOUT and/or STDERR are redirected
h <- createFile "CONOUT$" (gENERIC_WRITE .|. gENERIC_READ)
fILE_SHARE_WRITE Nothing oPEN_EXISTING 0 Nothing
when (h == iNVALID_HANDLE_VALUE ) $ fail "invalid handle value"
-- ref: https://docs.microsoft.com/en-us/windows/console/getconsolemode
m <- getConsoleMode h
-- VT processing not already enabled?
if m .&. eNABLE_VIRTUAL_TERMINAL_PROCESSING == 0
-- https://docs.microsoft.com/en-us/windows/console/setconsolemode
then setConsoleMode h (m .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING)
>> pure (Right False)
else pure (Right True)

View File

@@ -28,7 +28,7 @@ import qualified Data.Text as T
-- Note that when updating this, CI requires that the file exsists AND the same file exists at -- Note that when updating this, CI requires that the file exsists AND the same file exists at
-- 'https://www.haskell.org/ghcup/exp/ghcup-<ver>.yaml' with some newlines added. -- 'https://www.haskell.org/ghcup/exp/ghcup-<ver>.yaml' with some newlines added.
ghcupURL :: URI ghcupURL :: URI
ghcupURL = [uri|https://raw.githubusercontent.com/haskell/ghcup-metadata/master/ghcup-0.0.7.yaml|] ghcupURL = [uri|https://www.haskell.org/ghcup/data/ghcup-0.0.6.yaml|]
-- | The current ghcup version. -- | The current ghcup version.
ghcUpVer :: PVP ghcUpVer :: PVP

View File

@@ -0,0 +1,43 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveTraversable #-}
#if __GLASGOW_HASKELL__ >= 702
#define LANGUAGE_DeriveGeneric
{-# LANGUAGE DeriveGeneric #-}
#endif
module System.Console.Terminal.Common
( Window(..)
) where
import Data.Data (Typeable, Data)
#if __GLASGOW_HASKELL__ < 710
import Data.Foldable (Foldable)
import Data.Traversable (Traversable)
#endif
#ifdef LANGUAGE_DeriveGeneric
import GHC.Generics
( Generic
#if __GLASGOW_HASKELL__ >= 706
, Generic1
#endif
)
#endif
-- | Terminal window width and height
data Window a = Window
{ height :: !a
, width :: !a
} deriving
( Show, Eq, Read, Data, Typeable
, Foldable, Functor, Traversable
#ifdef LANGUAGE_DeriveGeneric
, Generic
#if __GLASGOW_HASKELL__ >= 706
, Generic1
#endif
#endif
)

View File

@@ -0,0 +1,65 @@
{-# LANGUAGE CApiFFI #-}
module System.Console.Terminal.Posix
( size, fdSize, hSize
) where
import System.Console.Terminal.Common
import Control.Exception (catch)
import Data.Typeable (cast)
import Foreign
import Foreign.C.Error
import Foreign.C.Types
import GHC.IO.FD (FD(FD, fdFD))
import GHC.IO.Handle.Internals (withHandle_)
import GHC.IO.Handle.Types (Handle, Handle__(Handle__, haDevice))
#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ < 706)
import Prelude hiding (catch)
#endif
import System.Posix.Types (Fd(Fd))
#include <sys/ioctl.h>
#include <unistd.h>
#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
-- Interesting part of @struct winsize@
data CWin = CWin CUShort CUShort
instance Storable CWin where
sizeOf _ = (#size struct winsize)
alignment _ = (#alignment struct winsize)
peek ptr = do
row <- (#peek struct winsize, ws_row) ptr
col <- (#peek struct winsize, ws_col) ptr
return $ CWin row col
poke ptr (CWin row col) = do
(#poke struct winsize, ws_row) ptr row
(#poke struct winsize, ws_col) ptr col
fdSize :: Integral n => Fd -> IO (Maybe (Window n))
fdSize (Fd fd) = with (CWin 0 0) $ \ws -> do
_ <- throwErrnoIfMinus1 "ioctl" $
ioctl fd (#const TIOCGWINSZ) ws
CWin row col <- peek ws
return . Just $ Window (fromIntegral row) (fromIntegral col)
`catch`
handler
where
handler :: IOError -> IO (Maybe (Window h))
handler _ = return Nothing
foreign import capi "sys/ioctl.h ioctl"
ioctl :: CInt -> CULong -> Ptr CWin -> IO CInt
size :: Integral n => IO (Maybe (Window n))
size = fdSize (Fd (#const STDOUT_FILENO))
hSize :: Integral n => Handle -> IO (Maybe (Window n))
hSize h = withHandle_ "hSize" h $ \Handle__ { haDevice = dev } ->
case cast dev of
Nothing -> return Nothing
Just FD { fdFD = fd } -> fdSize (Fd fd)

View File

@@ -1,6 +1,6 @@
site_name: GHCup site_name: GHCup
site_url: https://www.haskell.org/ghcup site_url: https://www.haskell.org/ghcup
site_description: GHCup is an installer for the general purpose language Haskell. site_description: GHCup documentation
site_author: GHCup Team site_author: GHCup Team
site_favicon: haskell_logo.png site_favicon: haskell_logo.png
@@ -13,8 +13,7 @@ theme:
nav: nav:
- Home: index.md - Home: index.md
- "Getting started": install.md - "Getting Started": install.md
- "First steps": steps.md
- "User Guide": guide.md - "User Guide": guide.md
- "Developer Guide": dev.md - "Developer Guide": dev.md
- About: about.md - About: about.md

View File

@@ -16,7 +16,6 @@
# * BOOTSTRAP_HASKELL_INSTALL_HLS - whether to install latest hls # * BOOTSTRAP_HASKELL_INSTALL_HLS - whether to install latest hls
# * BOOTSTRAP_HASKELL_ADJUST_BASHRC - whether to adjust PATH in bashrc (prepend) # * BOOTSTRAP_HASKELL_ADJUST_BASHRC - whether to adjust PATH in bashrc (prepend)
# * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows # * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows
# * GHCUP_BASE_URL - the base url for ghcup binary download (use this to overwrite https://downloads.haskell.org/~ghcup with a mirror)
# License: LGPL-3.0 # License: LGPL-3.0
@@ -26,8 +25,8 @@
plat="$(uname -s)" plat="$(uname -s)"
arch=$(uname -m) arch=$(uname -m)
ghver="0.1.17.8" ghver="0.1.17.2"
: "${GHCUP_BASE_URL:=https://downloads.haskell.org/~ghcup}" base_url="https://downloads.haskell.org/~ghcup"
export GHCUP_SKIP_UPDATE_CHECK=yes export GHCUP_SKIP_UPDATE_CHECK=yes
@@ -40,6 +39,7 @@ case "${plat}" in
;; ;;
*) *)
: "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}" : "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}"
export GHCUP_USE_XDG_DIRS
if [ -n "${GHCUP_USE_XDG_DIRS}" ] ; then if [ -n "${GHCUP_USE_XDG_DIRS}" ] ; then
GHCUP_DIR=${XDG_DATA_HOME:=$HOME/.local/share}/ghcup GHCUP_DIR=${XDG_DATA_HOME:=$HOME/.local/share}/ghcup
@@ -158,7 +158,7 @@ _done() {
green "and the \"Mingw package management docs\"" green "and the \"Mingw package management docs\""
green "desktop shortcuts." green "desktop shortcuts."
green green
green "If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/" green "If you are new to Haskell, check out https://www.haskell.org/ghcup/install/#first-steps"
;; ;;
*) *)
green green
@@ -173,7 +173,7 @@ _done() {
green "To install other GHC versions and tools, run:" green "To install other GHC versions and tools, run:"
green " ghcup tui" green " ghcup tui"
green green
green "If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/" green "If you are new to Haskell, check out https://www.haskell.org/ghcup/install/#first-steps"
;; ;;
esac esac
@@ -182,51 +182,6 @@ _done() {
exit 0 exit 0
} }
# @FUNCTION: posix_realpath
# @USAGE: <file>
# @DESCRIPTION:
# Portably gets the realpath and prints it to stdout.
# This was initially inspired by
# https://gist.github.com/tvlooy/cbfbdb111a4ebad8b93e
# and
# https://stackoverflow.com/a/246128
#
# If the file does not exist, just prints it appended to the current directory.
# @STDOUT: realpath of the given file
posix_realpath() {
[ -z "$1" ] && die "Internal error: no argument given to posix_realpath"
current_loop=0
max_loops=50
mysource=$1
# readlink and '[ -h $path ]' behave different wrt '/sbin/' and '/sbin', so we strip it
mysource=${mysource%/}
[ -z "${mysource}" ] && mysource=$1
while [ -h "${mysource}" ]; do
current_loop=$((current_loop+1))
mydir="$( cd -P "$( dirname "${mysource}" )" > /dev/null 2>&1 && pwd )"
mysource="$(readlink "${mysource}")"
[ "${mysource%"${mysource#?}"}"x != '/x' ] && mysource="${mydir%/}/${mysource}"
if [ ${current_loop} -gt ${max_loops} ] ; then
(>&2 echo "${1}: Too many levels of symbolic links")
echo "$1"
return
fi
done
mydir="$( cd -P "$( dirname "${mysource}" )" > /dev/null 2>&1 && pwd )"
# TODO: better distinguish between "does not exist" and "permission denied"
if [ -z "${mydir}" ] ; then
(>&2 echo "${1}: Permission denied")
echo "$(pwd)/$1"
else
echo "${mydir%/}/$(basename "${mysource}")"
fi
unset current_loop max_loops mysource mydir
}
download_ghcup() { download_ghcup() {
case "${plat}" in case "${plat}" in
@@ -236,26 +191,26 @@ download_ghcup() {
# we could be in a 32bit docker container, in which # we could be in a 32bit docker container, in which
# case uname doesn't give us what we want # case uname doesn't give us what we want
if [ "$(getconf LONG_BIT)" = "32" ] ; then if [ "$(getconf LONG_BIT)" = "32" ] ; then
_url=${GHCUP_BASE_URL}/${ghver}/i386-linux-ghcup-${ghver} _url=${base_url}/${ghver}/i386-linux-ghcup-${ghver}
elif [ "$(getconf LONG_BIT)" = "64" ] ; then elif [ "$(getconf LONG_BIT)" = "64" ] ; then
_url=${GHCUP_BASE_URL}/${ghver}/x86_64-linux-ghcup-${ghver} _url=${base_url}/${ghver}/x86_64-linux-ghcup-${ghver}
else else
die "Unknown long bit size: $(getconf LONG_BIT)" die "Unknown long bit size: $(getconf LONG_BIT)"
fi fi
;; ;;
i*86) i*86)
_url=${GHCUP_BASE_URL}/${ghver}/i386-linux-ghcup-${ghver} _url=${base_url}/${ghver}/i386-linux-ghcup-${ghver}
;; ;;
armv7*|*armv8l*) armv7*)
_url=${GHCUP_BASE_URL}/${ghver}/armv7-linux-ghcup-${ghver} _url=${base_url}/${ghver}/armv7-linux-ghcup-${ghver}
;; ;;
aarch64|arm64) aarch64|arm64|armv8l)
# we could be in a 32bit docker container, in which # we could be in a 32bit docker container, in which
# case uname doesn't give us what we want # case uname doesn't give us what we want
if [ "$(getconf LONG_BIT)" = "32" ] ; then if [ "$(getconf LONG_BIT)" = "32" ] ; then
_url=${GHCUP_BASE_URL}/${ghver}/armv7-linux-ghcup-${ghver} _url=${base_url}/${ghver}/armv7-linux-ghcup-${ghver}
elif [ "$(getconf LONG_BIT)" = "64" ] ; then elif [ "$(getconf LONG_BIT)" = "64" ] ; then
_url=${GHCUP_BASE_URL}/${ghver}/aarch64-linux-ghcup-${ghver} _url=${base_url}/${ghver}/aarch64-linux-ghcup-${ghver}
else else
die "Unknown long bit size: $(getconf LONG_BIT)" die "Unknown long bit size: $(getconf LONG_BIT)"
fi fi
@@ -282,15 +237,15 @@ download_ghcup() {
*) die "Unknown architecture: ${arch}" *) die "Unknown architecture: ${arch}"
;; ;;
esac esac
_url=${GHCUP_BASE_URL}/${ghver}/x86_64-freebsd${freebsd_ver}-ghcup-${ghver} _url=${base_url}/${ghver}/x86_64-portbld-freebsd${freebsd_ver}-ghcup-${ghver}
;; ;;
"Darwin"|"darwin") "Darwin"|"darwin")
case "${arch}" in case "${arch}" in
x86_64|amd64) x86_64|amd64)
_url=${GHCUP_BASE_URL}/${ghver}/x86_64-apple-darwin-ghcup-${ghver} _url=${base_url}/${ghver}/x86_64-apple-darwin-ghcup-${ghver}
;; ;;
aarch64|arm64|armv8l) aarch64|arm64|armv8l)
_url=${GHCUP_BASE_URL}/${ghver}/aarch64-apple-darwin-ghcup-${ghver} _url=${base_url}/${ghver}/aarch64-apple-darwin-ghcup-${ghver}
;; ;;
i*86) i*86)
die "i386 currently not supported!" die "i386 currently not supported!"
@@ -302,7 +257,7 @@ download_ghcup() {
MSYS*|MINGW*) MSYS*|MINGW*)
case "${arch}" in case "${arch}" in
x86_64|amd64) x86_64|amd64)
_url=${GHCUP_BASE_URL}/${ghver}/x86_64-mingw64-ghcup-${ghver}.exe _url=${base_url}/${ghver}/x86_64-mingw64-ghcup-${ghver}.exe
;; ;;
*) die "Unknown architecture: ${arch}" *) die "Unknown architecture: ${arch}"
;; ;;
@@ -326,20 +281,7 @@ download_ghcup() {
# we may overwrite this in adjust_bashrc # we may overwrite this in adjust_bashrc
cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file" cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file"
case ":\$PATH:" in export PATH="\$HOME/.cabal/bin:${GHCUP_BIN}:\$PATH"
*:"${GHCUP_BIN}":*)
;;
*)
export PATH="${GHCUP_BIN}:\$PATH"
;;
esac
case ":\$PATH:" in
*:"\$HOME/.cabal/bin":*)
;;
*)
export PATH="\$HOME/.cabal/bin:\$PATH"
;;
esac
EOF EOF
# shellcheck disable=SC1090 # shellcheck disable=SC1090
@@ -427,38 +369,12 @@ adjust_bashrc() {
case $1 in case $1 in
1) 1)
cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file" cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file"
case ":\$PATH:" in export PATH="\$HOME/.cabal/bin:${GHCUP_BIN}:\$PATH"
*:"${GHCUP_BIN}":*)
;;
*)
export PATH="${GHCUP_BIN}:\$PATH"
;;
esac
case ":\$PATH:" in
*:"\$HOME/.cabal/bin":*)
;;
*)
export PATH="\$HOME/.cabal/bin:\$PATH"
;;
esac
EOF EOF
;; ;;
2) 2)
cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file" cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file"
case ":\$PATH:" in export PATH="\$PATH:\$HOME/.cabal/bin:${GHCUP_BIN}"
*:"\$HOME/.cabal/bin":*)
;;
*)
export PATH="\$PATH:\$HOME/.cabal/bin"
;;
esac
case ":\$PATH:" in
*:"${GHCUP_BIN}":*)
;;
*)
export PATH="\$PATH:${GHCUP_BIN}"
;;
esac
EOF EOF
;; ;;
*) ;; *) ;;
@@ -473,23 +389,23 @@ adjust_bashrc() {
;; ;;
fish) fish)
mkdir -p "${GHCUP_PROFILE_FILE%/*}" mkdir -p "${GHCUP_PROFILE_FILE%/*}"
sed -i -e '/# ghcup-env$/ s/^#*/#/' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" sed -i -e '/# ghcup-env$/ s/^#*/#/' "${GHCUP_PROFILE_FILE}"
case $1 in case $1 in
1) 1)
printf "\n%s" "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME ; set -gx PATH \$HOME/.cabal/bin $GHCUP_BIN \$PATH # ghcup-env" >> "${GHCUP_PROFILE_FILE}" echo "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME ; set -gx PATH \$HOME/.cabal/bin $GHCUP_BIN \$PATH # ghcup-env" >> "${GHCUP_PROFILE_FILE}"
;; ;;
2) 2)
printf "\n%s" "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME ; set -gx PATH \$HOME/.cabal/bin \$PATH $GHCUP_BIN # ghcup-env" >> "${GHCUP_PROFILE_FILE}" echo "set -q GHCUP_INSTALL_BASE_PREFIX[1]; or set GHCUP_INSTALL_BASE_PREFIX \$HOME ; set -gx PATH \$HOME/.cabal/bin \$PATH $GHCUP_BIN # ghcup-env" >> "${GHCUP_PROFILE_FILE}"
;; ;;
esac esac
;; ;;
bash) bash)
sed -i -e '/# ghcup-env$/ s/^#*/#/' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" sed -i -e '/# ghcup-env$/ s/^#*/#/' "${GHCUP_PROFILE_FILE}"
printf "\n%s" "[ -f \"${GHCUP_DIR}/env\" ] && source \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}" echo "[ -f \"${GHCUP_DIR}/env\" ] && source \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}"
case "${plat}" in case "${plat}" in
"Darwin"|"darwin") "Darwin"|"darwin")
if ! grep -q "ghcup-env" "${HOME}/.bash_profile" ; then if ! grep -q "ghcup-env" "${HOME}/.bash_profile" ; then
printf "\n%s" "[[ -f ~/.bashrc ]] && source ~/.bashrc # ghcup-env" >> "${HOME}/.bash_profile" echo "[[ -f ~/.bashrc ]] && source ~/.bashrc # ghcup-env" >> "${HOME}/.bash_profile"
fi fi
;; ;;
MSYS*|MINGW*) MSYS*|MINGW*)
@@ -503,8 +419,8 @@ adjust_bashrc() {
;; ;;
zsh) zsh)
sed -i -e '/# ghcup-env$/ s/^#*/#/' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" sed -i -e '/# ghcup-env$/ s/^#*/#/' "${GHCUP_PROFILE_FILE}"
printf "\n%s" "[ -f \"${GHCUP_DIR}/env\" ] && source \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}" echo "[ -f \"${GHCUP_DIR}/env\" ] && source \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}"
;; ;;
esac esac
echo echo
@@ -571,7 +487,7 @@ ask_cabal_config_init() {
esac esac
done done
else else
return 0 return 1
fi fi
;; ;;
esac esac
@@ -589,8 +505,8 @@ do_cabal_config_init() {
adjust_cabal_config adjust_cabal_config
;; ;;
0) 0)
warn "Make sure that your global cabal.config references the correct mingw64 paths (extra-prog-path, extra-include-dirs and extra-lib-dirs)." echo "Make sure that your global cabal.config references the correct mingw64 paths (extra-prog-path, extra-include-dirs and extra-lib-dirs)."
warn "And set the environment variable GHCUP_MSYS2 to the root path of your msys2 installation." echo "And set the environment variable GHCUP_MSYS2 to the root path of your msys2 installation."
sleep 5 sleep 5
return ;; return ;;
*) ;; *) ;;
@@ -763,7 +679,7 @@ if [ -z "${BOOTSTRAP_HASKELL_MINIMAL}" ] ; then
do_cabal_config_init $ask_cabal_config_init_answer do_cabal_config_init $ask_cabal_config_init_answer
edo cabal new-update --ignore-project edo cabal new-update
else # don't install ghc and cabal else # don't install ghc and cabal
case "${plat}" in case "${plat}" in
MSYS*|MINGW*) MSYS*|MINGW*)

View File

@@ -29,11 +29,11 @@ param (
[switch]$InstallStack, [switch]$InstallStack,
# Whether to install hls as well # Whether to install hls as well
[switch]$InstallHLS, [switch]$InstallHLS,
# Specify the install root (default: 'C:\')
[string]$InstallDir,
# Specify the bootstrap url (default: 'https://www.haskell.org/ghcup/sh/bootstrap-haskell') # Specify the bootstrap url (default: 'https://www.haskell.org/ghcup/sh/bootstrap-haskell')
[string]$BootstrapUrl, [string]$InstallDir,
# Instead of installing a new MSys2, use an existing installation # Instead of installing a new MSys2, use an existing installation
[string]$BootstrapUrl,
# Specify the install root (default: 'C:\')
[string]$ExistingMsys2Dir, [string]$ExistingMsys2Dir,
# Specify the cabal root directory (default: '$InstallDir\cabal') # Specify the cabal root directory (default: '$InstallDir\cabal')
[string]$CabalDir [string]$CabalDir
@@ -239,34 +239,13 @@ if ($Silent -and !($InstallDir)) {
} }
} else { } else {
while ($true) { while ($true) {
Print-Msg -color Magenta -msg (@' Print-Msg -color Magenta -msg ('Where to install to (this should be a short Path, preferably a Drive like ''C:\''){1}Press enter to accept the default [{0}]:' -f $defaultGhcupBasePrefix, "`n")
Welcome to Haskell!
This script will download and install the following programs:
* ghcup - The Haskell toolchain installer
* ghc - The Glasgow Haskell Compiler
* msys2 - A linux-style toolchain environment required for many operations
* cabal - The Cabal build tool for managing Haskell software
* stack - (optional) A cross-platform program for developing Haskell projects
* hls - (optional) A language server for developers to integrate with their editor/IDE
Please note that ANTIVIRUS may interfere with the installation. If you experience problems, consider
disabling it temporarily.
Where to install to (this should be a short Path, preferably a Drive like 'C:\')?
If you accept this path, binaries will be installed into '{0}ghcup\bin' and msys2 into '{0}ghcup\msys64'.
Press enter to accept the default [{0}]:
'@ -f $defaultGhcupBasePrefix)
$basePrefixPrompt = Read-Host $basePrefixPrompt = Read-Host
$GhcupBasePrefix = ($defaultGhcupBasePrefix,$basePrefixPrompt)[[bool]$basePrefixPrompt] $GhcupBasePrefix = ($defaultGhcupBasePrefix,$basePrefixPrompt)[[bool]$basePrefixPrompt]
if (!($GhcupBasePrefix.EndsWith('\'))) { if (!($GhcupBasePrefix.EndsWith('\'))) {
$GhcupBasePrefix = ('{0}\' -f $GhcupBasePrefix) $GhcupBasePrefix = ('{0}\' -f $GhcupBasePrefix)
} }
$GhcupBasePrefix = $GhcupBasePrefix.TrimEnd().TrimStart()
if (!($GhcupBasePrefix)) { if (!($GhcupBasePrefix)) {
Print-Msg -color Red -msg "No directory specified!" Print-Msg -color Red -msg "No directory specified!"
} elseif (!(Test-Path -LiteralPath ('{0}' -f $GhcupBasePrefix))) { } elseif (!(Test-Path -LiteralPath ('{0}' -f $GhcupBasePrefix))) {
@@ -354,7 +333,6 @@ if ($CabalDir) {
$CabalDirPrompt = Read-Host $CabalDirPrompt = Read-Host
$CabDirEnv = ($defaultCabalDir,$CabalDirPrompt)[[bool]$CabalDirPrompt] $CabDirEnv = ($defaultCabalDir,$CabalDirPrompt)[[bool]$CabalDirPrompt]
$CabDirEnv = $CabDirEnv.TrimEnd().TrimStart()
if (!($CabDirEnv)) { if (!($CabDirEnv)) {
Print-Msg -color Red -msg "No directory specified!" Print-Msg -color Red -msg "No directory specified!"
} elseif (!(Split-Path -IsAbsolute -Path "$CabDirEnv")) { } elseif (!(Split-Path -IsAbsolute -Path "$CabDirEnv")) {
@@ -423,17 +401,16 @@ if (!(Test-Path -Path ('{0}' -f $MsysDir))) {
# Download the archive # Download the archive
Print-Msg -msg 'Downloading Msys2 archive...' Print-Msg -msg 'Downloading Msys2 archive...'
$archive = 'msys2-x86_64-latest.sfx.exe' $archive = 'msys2-x86_64-latest.sfx.exe'
$archivePath = ('{0}\{1}' -f ([IO.Path]::GetTempPath()), "$archive")
if (Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue) { if (Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue) {
Exec "curl.exe" '-o' "$archivePath" ('https://repo.msys2.org/distrib/{0}' -f "$archive") Exec "curl.exe" '-o' ('{0}\{1}' -f $env:TEMP, $archive) ('https://repo.msys2.org/distrib/{0}' -f $archive)
} else { } else {
Get-FileWCSynchronous -url ('https://repo.msys2.org/distrib/{0}' -f $archive) -destinationFolder ([IO.Path]::GetTempPath()) -includeStats Get-FileWCSynchronous -url ('https://repo.msys2.org/distrib/{0}' -f $archive) -destinationFolder "$env:TEMP" -includeStats
} }
Print-Msg -msg 'Extracting Msys2 archive...' Print-Msg -msg 'Extracting Msys2 archive...'
$null = & "$archivePath" '-y' ('-o{0}' -f $GhcupDir) # Extract $null = & "$env:TEMP\$archive" '-y' ('-o{0}' -f $GhcupDir) # Extract
Remove-Item -Path "$archivePath" Remove-Item -Path ('{0}/{1}' -f $env:TEMP, $archive)
Print-Msg -msg 'Processing MSYS2 bash for first time use...' Print-Msg -msg 'Processing MSYS2 bash for first time use...'
Exec "$Bash" '-lc' 'exit' Exec "$Bash" '-lc' 'exit'
@@ -467,7 +444,6 @@ if (!(Test-Path -Path ('{0}' -f $MsysDir))) {
Print-Msg -color Magenta -msg 'Input existing MSys2 toolchain directory:' Print-Msg -color Magenta -msg 'Input existing MSys2 toolchain directory:'
$MsysDir = Read-Host $MsysDir = Read-Host
} }
$MsysDir = $MsysDir.TrimEnd().TrimStart()
if (!($MsysDir)) { if (!($MsysDir)) {
Print-Msg -color Red -msg "No directory specified!" Print-Msg -color Red -msg "No directory specified!"
} elseif (!(Test-Path -LiteralPath ('{0}' -f $MsysDir))) { } elseif (!(Test-Path -LiteralPath ('{0}' -f $MsysDir))) {

View File

@@ -1,49 +0,0 @@
set -eu
tag=v$1
ver=$1
dest=$2
gpg_user=$3
mkdir -p "${dest}"
cd "${dest}"
base_url="https://gitlab.haskell.org/api/v4/projects/618/jobs/artifacts/${tag}/raw"
curl -f -o "x86_64-apple-darwin-ghcup-${ver}" \
"${base_url}/out/x86_64-apple-darwin-ghcup-${ver}?job=release:darwin"
curl -f -o "aarch64-apple-darwin-ghcup-${ver}" \
"${base_url}/out/aarch64-apple-darwin-ghcup-${ver}?job=release:darwin:aarch64"
curl -f -o "x86_64-freebsd12-ghcup-${ver}" \
"${base_url}/out/x86_64-portbld-freebsd-ghcup-${ver}?job=release:freebsd12"
curl -f -o "x86_64-freebsd13-ghcup-${ver}" \
"${base_url}/out/x86_64-portbld-freebsd-ghcup-${ver}?job=release:freebsd13"
curl -f -o "i386-linux-ghcup-${ver}" \
"${base_url}/out/i386-linux-ghcup-${ver}?job=release:linux:32bit"
curl -f -o "x86_64-linux-ghcup-${ver}" \
"${base_url}/out/x86_64-linux-ghcup-${ver}?job=release:linux:64bit"
curl -f -o "aarch64-linux-ghcup-${ver}" \
"${base_url}/out/aarch64-linux-ghcup-${ver}?job=release:linux:aarch64"
curl -f -o "armv7-linux-ghcup-${ver}" \
"${base_url}/out/armv7-linux-ghcup-${ver}?job=release:linux:armv7"
curl -f -o "x86_64-mingw64-ghcup-${ver}.exe" \
"${base_url}/out/x86_64-mingw64-ghcup-${ver}.exe?job=release:windows"
rm -f *.sig
sha256sum *-ghcup-* > SHA256SUMS
gpg --detach-sign -u ${gpg_user} SHA256SUMS
for f in *-ghcup-* ; do gpg --detach-sign -u ${gpg_user} $f ; done

View File

@@ -1,39 +0,0 @@
#!/bin/bash
url=$1
ver=$2
die() {
(>&2 printf "%s\\n" "$1")
exit 2
}
[ -z $url ] && die "no url set"
[ -z $ver ] && die "no version set"
sftp $url <<EOF
cd ghcup
rm aarch64-apple-darwin-ghcup
rm aarch64-linux-ghcup
rm armv7-linux-ghcup
rm i386-linux-ghcup
rm x86_64-apple-darwin-ghcup
rm x86_64-linux-ghcup
rm x86_64-mingw64-ghcup.exe
rm x86_64-freebsd12-ghcup
rm x86_64-freebsd13-ghcup
symlink ${ver}/aarch64-apple-darwin-ghcup-${ver} aarch64-apple-darwin-ghcup
symlink ${ver}/aarch64-linux-ghcup-${ver} aarch64-linux-ghcup
symlink ${ver}/armv7-linux-ghcup-${ver} armv7-linux-ghcup
symlink ${ver}/i386-linux-ghcup-${ver} i386-linux-ghcup
symlink ${ver}/x86_64-apple-darwin-ghcup-${ver} x86_64-apple-darwin-ghcup
symlink ${ver}/x86_64-freebsd12-ghcup-${ver} x86_64-freebsd12-ghcup
symlink ${ver}/x86_64-freebsd13-ghcup-${ver} x86_64-freebsd13-ghcup
symlink ${ver}/x86_64-linux-ghcup-${ver} x86_64-linux-ghcup
symlink ${ver}/x86_64-mingw64-ghcup-${ver}.exe x86_64-mingw64-ghcup.exe
EOF
curl -X PURGE https://downloads.haskell.org/~ghcup/
curl -X PURGE https://downloads.haskell.org/ghcup/

Some files were not shown because too many files have changed in this diff Show More