diff --git a/ghcup_install.sh b/ghcup_install.sh new file mode 100644 index 0000000..0350e27 --- /dev/null +++ b/ghcup_install.sh @@ -0,0 +1,1111 @@ +#!/bin/sh + +# This script downloads the 'ghcup' binary into '~/.ghcup/bin/' and then runs an interactive +# installation that lets you choose various options. Below is a list of environment variables +# that affect the installation procedure. + +# Main settings: +# * BOOTSTRAP_HASKELL_NONINTERACTIVE - any nonzero value for noninteractive installation +# * BOOTSTRAP_HASKELL_NO_UPGRADE - any nonzero value to not trigger the upgrade +# * BOOTSTRAP_HASKELL_MINIMAL - any nonzero value to only install ghcup +# * GHCUP_USE_XDG_DIRS - any nonzero value to respect The XDG Base Directory Specification +# * BOOTSTRAP_HASKELL_VERBOSE - any nonzero value for more verbose installation +# * BOOTSTRAP_HASKELL_GHC_VERSION - the ghc version to install +# * BOOTSTRAP_HASKELL_CABAL_VERSION - the cabal version to install +# * BOOTSTRAP_HASKELL_CABAL_XDG - don't disable the XDG logic (this doesn't force XDG though, because cabal is confusing) +# * BOOTSTRAP_HASKELL_INSTALL_NO_STACK - disable installation of stack +# * BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK - disable installation stack ghcup hook +# * BOOTSTRAP_HASKELL_STACK_VERSION - the stack version to install +# * BOOTSTRAP_HASKELL_INSTALL_HLS - whether to install hls +# * BOOTSTRAP_HASKELL_HLS_VERSION - the hls version to install +# * 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_DOWNLOADER - which downloader to use (default: curl) +# * GHCUP_BASE_URL - the base url for ghcup binary download (use this to overwrite https://downloads.haskell.org/~ghcup with a mirror) +# * GHCUP_MSYS2_ENV - the msys2 environment to use on windows, see https://www.msys2.org/docs/environments/ (defauts to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) + +# License: LGPL-3.0 + + +# safety subshell to avoid executing anything in case this script is not downloaded properly +( + +die() { + if [ -n "${NO_COLOR}" ] ; then + (>&2 printf "%s\\n" "$1") + else + (>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1") + fi + exit 2 +} + +plat="$(uname -s)" +arch=$(uname -m) +ghver="0.1.50.2" +: "${GHCUP_BASE_URL:=https://downloads.haskell.org/~ghcup}" + +export GHCUP_SKIP_UPDATE_CHECK=yes +: "${BOOTSTRAP_HASKELL_DOWNLOADER:=curl}" + +set_msys2_env_dir() { + case "${GHCUP_MSYS2_ENV}" in + "") + case "${arch}" in + x86_64|amd64) + GHCUP_MSYS2_ENV_DIR="mingw64" ;; + i*86) + GHCUP_MSYS2_ENV_DIR="mingw32" ;; + aarch64|arm64) + GHCUP_MSYS2_ENV_DIR="clangarm64" ;; + *) die "Unknown architecture: ${arch}" ;; + esac + ;; + MSYS) + GHCUP_MSYS2_ENV_DIR="usr" ;; + UCRT64) + GHCUP_MSYS2_ENV_DIR="ucrt64" ;; + CLANG64) + GHCUP_MSYS2_ENV_DIR="clang64" ;; + CLANGARM64) + GHCUP_MSYS2_ENV_DIR="clangarm64" ;; + CLANG32) + GHCUP_MSYS2_ENV_DIR="clang32" ;; + MINGW64) + GHCUP_MSYS2_ENV_DIR="mingw64" ;; + MINGW32) + GHCUP_MSYS2_ENV_DIR="mingw32" ;; + *) + die "Invalid value for GHCUP_MSYS2_ENV. Valid values are: MSYS, UCRT64, CLANG64, CLANGARM64, CLANG32, MINGW64, MINGW32" ;; + esac +} + +case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + : "${GHCUP_INSTALL_BASE_PREFIX:=/c}" + GHCUP_DIR=$(cygpath -u "${GHCUP_INSTALL_BASE_PREFIX}/ghcup") + GHCUP_BIN=$(cygpath -u "${GHCUP_INSTALL_BASE_PREFIX}/ghcup/bin") + : "${GHCUP_MSYS2:=${GHCUP_DIR}/msys64}" + set_msys2_env_dir + ;; + *) + : "${GHCUP_INSTALL_BASE_PREFIX:=$HOME}" + + if [ -n "${GHCUP_USE_XDG_DIRS}" ] ; then + GHCUP_DIR=${XDG_DATA_HOME:=$HOME/.local/share}/ghcup + GHCUP_BIN=${XDG_BIN_HOME:=$HOME/.local/bin} + else + GHCUP_DIR=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup + GHCUP_BIN=${GHCUP_INSTALL_BASE_PREFIX}/.ghcup/bin + fi + ;; +esac + +: "${BOOTSTRAP_HASKELL_GHC_VERSION:=recommended}" +: "${BOOTSTRAP_HASKELL_CABAL_VERSION:=recommended}" +: "${BOOTSTRAP_HASKELL_STACK_VERSION:=recommended}" +: "${BOOTSTRAP_HASKELL_HLS_VERSION:=recommended}" + +warn() { + if [ -n "${NO_COLOR}" ] ; then + printf "%s\\n" "$1" + else + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + # shellcheck disable=SC3037 + echo -e "\\033[0;35m$1\\033[0m" + ;; + *) + printf "\\033[0;35m%s\\033[0m\\n" "$1" + ;; + esac + fi +} + +yellow() { + if [ -n "${NO_COLOR}" ] ; then + printf "%s\\n" "$1" + else + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + # shellcheck disable=SC3037 + echo -e "\\033[0;33m$1\\033[0m" + ;; + *) + printf "\\033[0;33m%s\\033[0m\\n" "$1" + ;; + esac + fi +} + +green() { + if [ -n "${NO_COLOR}" ] ; then + printf "%s\\n" "$1" + else + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + # shellcheck disable=SC3037 + echo -e "\\033[0;32m$1\\033[0m" + ;; + *) + printf "\\033[0;32m%s\\033[0m\\n" "$1" + ;; + esac + fi +} + +edo() { + "$@" || die "\"$*\" failed!" +} + +eghcup_raw() { + "${GHCUP_BIN}/ghcup" "$@" || die "\"ghcup $*\" failed!" +} + +eghcup() { + _eghcup "$@" +} + +_eghcup() { + if [ -n "${BOOTSTRAP_HASKELL_YAML}" ] ; then + args="-s ${BOOTSTRAP_HASKELL_YAML} --metadata-fetching-mode=Strict" + else + args="--metadata-fetching-mode=Strict" + fi + if [ -z "${BOOTSTRAP_HASKELL_VERBOSE}" ] ; then + # shellcheck disable=SC2086 + "${GHCUP_BIN}/ghcup" ${args} "$@" || die "\"ghcup ${args} $*\" failed!" + else + # shellcheck disable=SC2086 + "${GHCUP_BIN}/ghcup" ${args} --verbose "$@" || die "\"ghcup ${args} --verbose $*\" failed!" + fi +} + +_ecabal() { + # shellcheck disable=SC2317 + if [ -n "${CABAL_BIN}" ] ; then + "${CABAL_BIN}" "$@" + else + # shellcheck disable=SC2086 + "${GHCUP_BIN}/cabal" "$@" + fi +} + +ecabal() { + _ecabal "$@" || die "\"cabal $*\" failed!" +} + +_done() { + echo + echo "===============================================================================" + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + green + green "All done!" + green + green "In a new powershell or cmd.exe session, now you can..." + green + green "Start a simple repl via:" + green " ghci" + green + green "Start a new haskell project in the current directory via:" + green " cabal init --interactive" + green + green "To install other GHC versions and tools, run:" + green " ghcup tui" + green + green "To install system libraries and update msys2/mingw64," + green "open the \"Mingw haskell shell\"" + green "and the \"Mingw package management docs\"" + green "desktop shortcuts." + green + green "If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/" + ;; + *) + green + green "All done!" + green + green "To start a simple repl, run:" + green " ghci" + green + green "To start a new haskell project in the current directory, run:" + green " cabal init --interactive" + green + green "To install other GHC versions and tools, run:" + green " ghcup tui" + green + green "If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/" + ;; + + esac + + + exit 0 +} + +# @FUNCTION: posix_realpath +# @USAGE: +# @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() { + + case "${plat}" in + "linux"|"Linux") + case "${arch}" in + x86_64|amd64) + # we could be in a 32bit docker container, in which + # case uname doesn't give us what we want + if [ "$(getconf LONG_BIT)" = "32" ] ; then + _url=${GHCUP_BASE_URL}/${ghver}/i386-linux-ghcup-${ghver} + elif [ "$(getconf LONG_BIT)" = "64" ] ; then + _url=${GHCUP_BASE_URL}/${ghver}/x86_64-linux-ghcup-${ghver} + else + die "Unknown long bit size: $(getconf LONG_BIT)" + fi + ;; + i*86) + _url=${GHCUP_BASE_URL}/${ghver}/i386-linux-ghcup-${ghver} + ;; + armv7*|*armv8l*) + # later versions don't support armv7 anymore + _url=${GHCUP_BASE_URL}/0.1.40.0/armv7-linux-ghcup-0.1.40.0 + ;; + aarch64|arm64) + # we could be in a 32bit docker container, in which + # case uname doesn't give us what we want + if [ "$(getconf LONG_BIT)" = "32" ] ; then + # later versions don't support armv7 anymore + _url=${GHCUP_BASE_URL}/0.1.40.0/armv7-linux-ghcup-0.1.40.0 + elif [ "$(getconf LONG_BIT)" = "64" ] ; then + _url=${GHCUP_BASE_URL}/${ghver}/aarch64-linux-ghcup-${ghver} + else + die "Unknown long bit size: $(getconf LONG_BIT)" + fi + ;; + *) die "Unknown architecture: ${arch}" + ;; + esac + ;; + "FreeBSD"|"freebsd") + case "${arch}" in + x86_64|amd64) + ;; + i*86) + die "i386 currently not supported!" + ;; + *) die "Unknown architecture: ${arch}" + ;; + esac + _url=${GHCUP_BASE_URL}/${ghver}/x86_64-portbld-freebsd-ghcup-${ghver} + ;; + "Darwin"|"darwin") + case "${arch}" in + x86_64|amd64) + _url=${GHCUP_BASE_URL}/${ghver}/x86_64-apple-darwin-ghcup-${ghver} + ;; + aarch64|arm64|armv8l) + _url=${GHCUP_BASE_URL}/${ghver}/aarch64-apple-darwin-ghcup-${ghver} + ;; + i*86) + die "i386 currently not supported!" + ;; + *) die "Unknown architecture: ${arch}" + ;; + esac + ;; + MSYS*|MINGW*|CYGWIN*) + case "${arch}" in + x86_64|amd64) + _url=${GHCUP_BASE_URL}/${ghver}/x86_64-mingw64-ghcup-${ghver}.exe + ;; + *) die "Unknown architecture: ${arch}" + ;; + esac + ;; + *) die "Unknown platform: ${plat}" + ;; + esac + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + case "${BOOTSTRAP_HASKELL_DOWNLOADER}" in + "curl") + # shellcheck disable=SC2086 + edo curl -Lf ${GHCUP_CURL_OPTS} "${_url}" > "${GHCUP_BIN}"/ghcup.exe + ;; + "wget") + # shellcheck disable=SC2086 + edo wget -O /dev/stdout ${GHCUP_WGET_OPTS} "${_url}" > "${GHCUP_BIN}"/ghcup.exe + ;; + *) + die "Unknown downloader: ${BOOTSTRAP_HASKELL_DOWNLOADER}" + ;; + esac + edo chmod +x "${GHCUP_BIN}"/ghcup.exe + ;; + *) + case "${BOOTSTRAP_HASKELL_DOWNLOADER}" in + "curl") + # shellcheck disable=SC2086 + edo curl -Lf ${GHCUP_CURL_OPTS} "${_url}" > "${GHCUP_BIN}"/ghcup + ;; + "wget") + # shellcheck disable=SC2086 + edo wget -O /dev/stdout ${GHCUP_WGET_OPTS} "${_url}" > "${GHCUP_BIN}"/ghcup + ;; + *) + die "Unknown downloader: ${BOOTSTRAP_HASKELL_DOWNLOADER}" + ;; + esac + edo chmod +x "${GHCUP_BIN}"/ghcup + ;; + esac + + edo mkdir -p "${GHCUP_DIR}" + + # we may overwrite this in adjust_bashrc + cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file" + case ":\$PATH:" in + *:"${GHCUP_BIN}":*) + ;; + *) + export PATH="${GHCUP_BIN}:\$PATH" + ;; + esac + case ":\$PATH:" in + *:"\$HOME/.cabal/bin":*) + ;; + *) + export PATH="\$HOME/.cabal/bin:\$PATH" + ;; + esac + EOF + + # shellcheck disable=SC1090 + edo . "${GHCUP_DIR}"/env + case "${BOOTSTRAP_HASKELL_DOWNLOADER}" in + "curl") + eghcup_raw config set downloader Curl + ;; + "wget") + eghcup_raw config set downloader Wget + ;; + *) + die "Unknown downloader: ${BOOTSTRAP_HASKELL_DOWNLOADER}" + ;; + esac + eghcup upgrade +} + +# Figures out the users login shell and sets +# GHCUP_PROFILE_FILE and MY_SHELL variables. +find_shell() { + case $SHELL in + */zsh) # login shell is zsh + if [ -n "$ZDOTDIR" ]; then + GHCUP_PROFILE_FILE="$ZDOTDIR/.zshrc" + else + GHCUP_PROFILE_FILE="$HOME/.zshrc" + fi + MY_SHELL="zsh" ;; + */bash) # login shell is bash + GHCUP_PROFILE_FILE="$HOME/.bashrc" + MY_SHELL="bash" ;; + */sh) # login shell is sh, but might be a symlink to bash or zsh + if [ -n "${BASH}" ] ; then + GHCUP_PROFILE_FILE="$HOME/.bashrc" + MY_SHELL="bash" + elif [ -n "${ZSH_VERSION}" ] ; then + GHCUP_PROFILE_FILE="$HOME/.zshrc" + MY_SHELL="zsh" + else + return + fi + ;; + */fish) # login shell is fish + GHCUP_PROFILE_FILE="$HOME/.config/fish/config.fish" + MY_SHELL="fish" ;; + *) return ;; + esac +} + +ask_base_channel() { + if [ -n "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then + return 0 + fi + echo "-------------------------------------------------------------------------------" + warn "" + warn "GHCup provides different binary distribution \"channels\". These are collections of tools" + warn "and may differ in purpose and philosophy. First, we select the base channel." + warn "" + while true; do + + warn "[S] Skip [D] Default (GHCup maintained) [V] Vanilla (Upstream maintained) [?] Help (default is \"Skip\")." + warn "" + + read -r bashrc_answer "${GHCUP_DIR}"/env || die "Failed to create env file" + case ":\$PATH:" in + *:"${GHCUP_BIN}":*) + ;; + *) + export PATH="${GHCUP_BIN}:\$PATH" + ;; + esac + case ":\$PATH:" in + *:"\$HOME/.cabal/bin":*) + ;; + *) + export PATH="\$HOME/.cabal/bin:\$PATH" + ;; + esac + EOF + ;; + 2) + cat <<-EOF > "${GHCUP_DIR}"/env || die "Failed to create env file" + case ":\$PATH:" in + *:"\$HOME/.cabal/bin":*) + ;; + *) + export PATH="\$PATH:\$HOME/.cabal/bin" + ;; + esac + case ":\$PATH:" in + *:"${GHCUP_BIN}":*) + ;; + *) + export PATH="\$PATH:${GHCUP_BIN}" + ;; + esac + EOF + ;; + *) ;; + esac + + case $1 in + 1 | 2) + case $MY_SHELL in + "") + warn_path "Couldn't figure out login shell!" + return + ;; + fish) + mkdir -p "${GHCUP_PROFILE_FILE%/*}" + sed -i -e '/# ghcup-env$/d' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" + case $1 in + 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}" + ;; + 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}" + ;; + esac + ;; + bash) + sed -i -e '/# ghcup-env$/d' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" + printf "\n%s" "[ -f \"${GHCUP_DIR}/env\" ] && . \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}" + case "${plat}" in + "Darwin"|"darwin") + if ! grep -q "ghcup-env" "${HOME}/.bash_profile" ; then + printf "\n%s" "[[ -f ~/.bashrc ]] && . ~/.bashrc # ghcup-env" >> "${HOME}/.bash_profile" + fi + ;; + MSYS*|MINGW*|CYGWIN*) + if [ ! -e "${HOME}/.bash_profile" ] ; then + echo '# generated by ghcup' > "${HOME}/.bash_profile" + echo 'test -f ~/.profile && . ~/.profile' >> "${HOME}/.bash_profile" + echo 'test -f ~/.bashrc && . ~/.bashrc' >> "${HOME}/.bash_profile" + fi + ;; + esac + ;; + + zsh) + sed -i -e '/# ghcup-env$/d' "$(posix_realpath "${GHCUP_PROFILE_FILE}")" + printf "\n%s" "[ -f \"${GHCUP_DIR}/env\" ] && . \"${GHCUP_DIR}/env\" # ghcup-env" >> "${GHCUP_PROFILE_FILE}" + ;; + esac + if [ -e "$HOME/.profile" ] ; then + sed -i -e '/# ghcup-env$/d' "$(posix_realpath "$HOME/.profile")" + printf "\n%s" "[ -f \"${GHCUP_DIR}/env\" ] && . \"${GHCUP_DIR}/env\" # ghcup-env" >> "$HOME/.profile" + fi + echo + echo "===============================================================================" + echo + warn "OK! ${GHCUP_PROFILE_FILE} has been modified. Restart your terminal for the changes to take effect," + warn "or type \". ${GHCUP_DIR}/env\" to apply them in your current terminal session." + return + ;; + *) + warn_path + ;; + esac +} + +warn_path() { + echo + echo "===============================================================================" + echo + [ -n "$1" ] && warn "$1" + yellow "In order to run ghc and cabal, you need to adjust your PATH variable." + yellow "To do so, you may want to run 'source $GHCUP_DIR/env' in your current terminal" + yellow "session as well as your shell configuration (e.g. ~/.bashrc)." + +} + +adjust_cabal_config() { + if [ -n "${CABAL_DIR}" ] ; then + cabal_bin="${CABAL_DIR}/bin" + else + cabal_bin="$HOME/AppData/Roaming/cabal/bin" + fi + ecabal user-config -a "extra-prog-path: $(cygpath -w "$GHCUP_BIN"), $(cygpath -w "$cabal_bin"), $(cygpath -w "$GHCUP_MSYS2/${GHCUP_MSYS2_ENV_DIR}/bin"), $(cygpath -w "$GHCUP_MSYS2"/usr/bin)" -a "extra-include-dirs: $(cygpath -w "$GHCUP_MSYS2/${GHCUP_MSYS2_ENV_DIR}/include")" -a "extra-lib-dirs: $(cygpath -w "$GHCUP_MSYS2/${GHCUP_MSYS2_ENV_DIR}/lib")" -f init +} + +ask_cabal_config_init() { + case "${plat}" in + MSYS*|MINGW*|CYGWIN*) + if [ -n "${BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG}" ] ; then + return 1 + fi + + if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then + echo "-------------------------------------------------------------------------------" + warn "Create an initial cabal.config including relevant msys2 paths (recommended)?" + warn "[Y] Yes [N] No [?] Help (default is \"Y\")." + echo + while true; do + read -r mingw_answer /dev/null 2>&1 ; then + if [ -z "${BOOTSTRAP_HASKELL_NO_UPGRADE}" ] ; then + ( _eghcup upgrade ) || download_ghcup + fi +else + download_ghcup +fi + +echo +if [ -n "${BOOTSTRAP_HASKELL_YAML}" ] ; then (>&2 ghcup -s "${BOOTSTRAP_HASKELL_YAML}" tool-requirements) ; else (>&2 ghcup tool-requirements) ; fi +echo + +if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then + echo + warn "Press ENTER to proceed or ctrl-c to abort." + echo + + # Wait for user input to continue. + # shellcheck disable=SC2034 + read -r answer "${hook_exe}" + ;; + "wget") + # shellcheck disable=SC2086 + edo wget -O /dev/stdout ${GHCUP_WGET_OPTS} "${hook_url}" > "${hook_exe}" + ;; + *) + die "Unknown downloader: ${BOOTSTRAP_HASKELL_DOWNLOADER}" + ;; + esac + edo chmod +x "${hook_exe}" + fi + + ;; + *) ;; +esac + + +adjust_bashrc $ask_bashrc_answer + + +_done + +) + +# vim: tabstop=4 shiftwidth=4 expandtab diff --git a/package.yaml b/package.yaml index 0711b77..b85e49e 100644 --- a/package.yaml +++ b/package.yaml @@ -29,7 +29,8 @@ dependencies: library: source-dirs: src dependencies: - - ghc >= 8.10.2 && <9.1 + - ghc >= 8.10.2 && <9.5 + - ghc-tcplugins-extra >= 0.4 && <0.6 - containers >= 0.6.2.1 - term-rewriting >= 0.3.0.1 - transformers >= 0.5.6.2 diff --git a/src/TypeLevel/Rewrite.hs b/src/TypeLevel/Rewrite.hs index 27759f6..7b49e80 100644 --- a/src/TypeLevel/Rewrite.hs +++ b/src/TypeLevel/Rewrite.hs @@ -17,15 +17,15 @@ import GHC.Tc.Types.Evidence (EvExpr, EvTerm, evCast) import GHC.Tc.Plugin (newWanted) import GHC.Core.TyCo.Rep (UnivCoProvenance(PluginProv)) import GHC.Plugins (synTyConDefn_maybe) -import GHC.Tc.Types (TcPluginResult(..), TcPluginM, ErrCtxt, pushErrCtxtSameOrigin, TcPlugin(..)) +import GHC.Tc.Types () #else import Coercion (Role(Representational), mkUnivCo) import Constraint (CtEvidence(ctev_loc), Ct, ctEvExpr, ctLoc, mkNonCanonical) import GhcPlugins (PredType, SDoc, eqType, fsep, ppr) import Plugins (Plugin(pluginRecompile, tcPlugin), CommandLineOption, defaultPlugin, purePlugin) import TcEvidence (EvExpr, EvTerm, evCast) -import TcPluginM (newWanted) -import TcRnTypes +import TcPluginM (newWanted, TcPluginM) +import TcRnTypes (TcPluginResult(..), ErrCtxt, pushErrCtxtSameOrigin, TcPlugin(..)) import TyCoRep (UnivCoProvenance(PluginProv)) import TyCon (synTyConDefn_maybe) #endif diff --git a/src/TypeLevel/Rewrite/Internal/Lookup.hs b/src/TypeLevel/Rewrite/Internal/Lookup.hs index c390638..677a994 100644 --- a/src/TypeLevel/Rewrite/Internal/Lookup.hs +++ b/src/TypeLevel/Rewrite/Internal/Lookup.hs @@ -4,71 +4,38 @@ module TypeLevel.Rewrite.Internal.Lookup where import Control.Arrow ((***), first) import Data.Tuple (swap) +import GHC.TcPluginM.Extra (lookupModule, lookupName) + -- GHC API import GHC (DataCon, TyCon, dataConTyCon) #if MIN_VERSION_ghc(9,0,0) -import GHC.Driver.Finder (cannotFindModule) -import GHC (Module, ModuleName, mkModuleName) -import GHC.Plugins (mkDataOcc, mkTcOcc) -import GHC.Utils.Panic (panicDoc) -import GHC.Tc.Plugin - ( FindResult(Found), TcPluginM, findImportedModule, lookupOrig, tcLookupDataCon, tcLookupTyCon - , unsafeTcPluginTcM - ) -import GHC.Tc.Solver.Monad (getDynFlags) +import GHC.Plugins (mkModuleName, mkTcOcc, mkDataOcc) +import GHC.Tc.Plugin (TcPluginM, tcLookupTyCon, tcLookupDataCon) +import GHC.Data.FastString (fsLit) #else -import Finder (cannotFindModule) -import Module (Module, ModuleName, mkModuleName) -import OccName (mkDataOcc, mkTcOcc) -import Panic (panicDoc) -import TcPluginM -import TcSMonad (getDynFlags) +import Module (mkModuleName) +import OccName (mkTcOcc, mkDataOcc) +import TcPluginM (TcPluginM, tcLookupTyCon, tcLookupDataCon) +import FastString (fsLit) #endif -lookupModule - :: String -- ^ module name - -> TcPluginM Module -lookupModule moduleNameStr = do - let moduleName :: ModuleName - moduleName = mkModuleName moduleNameStr - findImportedModule moduleName Nothing >>= \case - Found _ module_ -> do - pure module_ - findResult -> do - dynFlags <- unsafeTcPluginTcM getDynFlags - panicDoc ("TypeLevel.Lookup.lookupModule " ++ show moduleNameStr) - $ cannotFindModule dynFlags moduleName findResult - --- 'TcPluginM.lookupM' unfortunately fails with a very unhelpful error message --- when we look up a name which doesn't exist: --- --- Can't find interface-file declaration for type constructor or class ModuleName.TypeName --- Probable cause: bug in .hi-boot file, or inconsistent .hi file --- Use -ddump-if-trace to get an idea of which file caused the error --- --- But the true cause isn't a corrupted file, it's simply that the requested --- name is not in the given module. I don't know how to fix the error message --- (I can't use 'try' nor 'tryM' because we're in the wrong monad) - lookupTyCon :: String -- ^ module name -> String -- ^ type constructor/family name -> TcPluginM TyCon lookupTyCon moduleNameStr tyConNameStr = do - module_ <- lookupModule moduleNameStr - tyConName <- lookupOrig module_ (mkTcOcc tyConNameStr) - tyCon <- tcLookupTyCon tyConName - pure tyCon + module_ <- lookupModule (mkModuleName moduleNameStr) (fsLit "") + tyConName <- lookupName module_ (mkTcOcc tyConNameStr) + tcLookupTyCon tyConName lookupDataCon :: String -- ^ module name -> String -- ^ data constructor name -> TcPluginM DataCon lookupDataCon moduleNameStr dataConNameStr = do - module_ <- lookupModule moduleNameStr - dataConName <- lookupOrig module_ (mkDataOcc dataConNameStr) - dataCon <- tcLookupDataCon dataConName - pure dataCon + module_ <- lookupModule (mkModuleName moduleNameStr) (fsLit "") + dataConName <- lookupName module_ (mkDataOcc dataConNameStr) + tcLookupDataCon dataConName splitFirstDot diff --git a/typelevel-rewrite-rules.cabal b/typelevel-rewrite-rules.cabal index bc876cb..ed3decf 100644 --- a/typelevel-rewrite-rules.cabal +++ b/typelevel-rewrite-rules.cabal @@ -4,7 +4,7 @@ cabal-version: 1.12 -- -- see: https://github.com/sol/hpack -- --- hash: 228b6969b9e5934cdf11b460c5fa1ee3e3f5e70e3f58d4b5422a80a9676c49b3 +-- hash: ed895af85dd6e7683ed3df4f1aa346fd35ea03fa61ead905d9a9938ff88a572d name: typelevel-rewrite-rules version: 1.0.0.1 @@ -51,8 +51,9 @@ library build-depends: base >=4.12 && <5 , containers >=0.6.2.1 - , ghc >=8.10.2 && <9.1 + , ghc >=8.10.2 && <9.5 , ghc-prim >=0.5.3 + , ghc-tcplugins-extra >=0.4 && <0.6 , term-rewriting >=0.3.0.1 , transformers >=0.5.6.2 default-language: Haskell2010