diff --git a/README.md b/README.md index 9805134996211d8548311f2985c6aaf06994bd05..394e5f2048510023cf867b49242d08acc85c7fdb 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ handles your haskell packages and can demand that [a specific version](https://c Installs a specified GHC version into `~/.ghcup/ghc/<ver>`, and places `ghc-<ver>` symlinks in `~/.ghcup/bin/`. -Optionally, an unversioned `ghc` link can point to a default version of your choice. +Optionally, unversioned `ghc` shims can be created that respect `GHCUP_GHCVER` env variable, by default read the ghc version from `~/.ghcup/.ghcup.ghcver` and can also read `.ghcup.ghcver` from the current directory. This uses precompiled GHC binaries that have been compiled on fedora/debian by [upstream GHC](https://www.haskell.org/ghc/download_ghc_8_6_1.html#binaries). diff --git a/ghcup b/ghcup index e66e998bc234b09b542f0a54d09db64574d93cb9..3f675373606658594838b85ea3846556a639d6b8 100755 --- a/ghcup +++ b/ghcup @@ -243,6 +243,7 @@ USAGE: FLAGS: -h, --help Prints help information + -l, --local Set the GHC version for the current project/directory only ARGS: [VERSION|TAG] E.g. \"8.4.3\" or \"8.6.3\" or @@ -250,9 +251,16 @@ ARGS: (default: discovers recommended version) DISCUSSION: - Sets the the current GHC version by creating non-versioned - symlinks for all ghc binaries of the specified version in - \"~/.ghcup/bin/<binary>\". + Sets the current GHC version by creating a file + .ghcup.ghcver either in the current directory (if -l is supplied) + or globally in ~/.ghcup directory and then creating shims + for ghc/ghci and friends that read these files. The decision + as to which GHC version to use is as follows: + + 1. if GHCUP_GHCVER env variable is set, use that as ghc version + 2. if .ghcup.ghcver exists in current directory, read the contents of + that file as ghc version + 3. else use ~/.ghcup/.ghcup.ghcver ") exit 1 } @@ -1145,7 +1153,7 @@ get_meta_download_file() { # META_VERSION_URL. # @STDOUT: known ghc versions known_tool_versions() { - [ -z "$1" ] && die "Internal error: no argument given to posix_realpath" + [ -z "$1" ] && die "Internal error: no argument given to known_tool_versions" mytool=$1 meta_file="$(get_meta_version_file)" @@ -1216,6 +1224,8 @@ array_contains() { ############################ + + # @FUNCTION: install_ghc # @USAGE: <ghcversion> # @DESCRIPTION: @@ -1308,34 +1318,99 @@ install_ghc() { ######################## +# @FUNCTION: create_shim +# @USAGE: <target> +# @DESCRIPTION: +# Creates a shim for the given target (like 'ghc'), +# which can dynamically choose a specific requested version. +create_shim() { + [ -z "$1" ] && die "Internal error: no argument given to create_shim" + + shim_target=$1 + + debug_message "creating target shim for ${shim_target}" + + cat <<-EOF > "${BIN_LOCATION}/${shim_target}" || die + #!/bin/sh + + target="${shim_target}" + + GHCUP_BASE="$INSTALL_BASE" + + if [ -n "\${GHCUP_GHCVER}" ] ; then + exec "\${target}-\${GHCUP_GHCVER}" "\$@" + elif [ -f "\${PWD}/.ghcup.ghcver" ] ; then + ghcver="\$(cat "\${PWD}/.ghcup.ghcver")" + + exec "\${target}-\${ghcver}" "\$@" + else + [ -f "\${GHCUP_BASE}/.ghcup.ghcver" ] || { + (>&2 echo "\${GHCUP_BASE}/.ghcup.ghcver missing! Don't know what version to run. You may need to issue: 'ghcup set <ghcver>'") + exit 1 + } + + ghcver="\$(cat "\${GHCUP_BASE}/.ghcup.ghcver")" + + exec "\${target}-\${ghcver}" "\$@" + fi + EOF + + edo chmod +x "${BIN_LOCATION}/${shim_target}" + + unset shim_target +} + + + + # @FUNCTION: set_ghc -# @USAGE: <ghcversion> +# @USAGE: <ghcversion> <local-or-not> # @DESCRIPTION: -# Sets the current ghc version by creating symlinks. +# Sets the current ghc version by creating shims and ghc version config file. set_ghc() { - [ -z "$1" ] && die "Internal error: no argument given to set_ghc" + { [ -z "$1" ] || [ -z "$2" ] ;} && die "Internal error: not enough arguments given to set_ghc" myghcver=$1 + is_local=$2 inst_location=$(get_ghc_location "$1") [ -z "${inst_location}" ] && die "failed to get install location" [ -e "${inst_location}" ] || die "GHC ${myghcver} not installed yet, use: ${SCRIPT} install ${myghcver}" - status_message "Setting GHC to ${myghcver}" + if ${is_local} ; then + status_message "Setting local GHC to ${myghcver}" + else + status_message "Setting global GHC to ${myghcver}" + fi + # create shims for f in "${inst_location}"/bin/*-"${myghcver}" ; do [ -e "${f}" ] || die "Something went wrong, ${f} does not exist!" source_fn=$(basename "${f}") target_fn=$(echo "${source_fn}" | sed "s#-${myghcver}##") - # shellcheck disable=SC2046 - edo ln $(optionv "-v") -sf ../ghc/"${myghcver}/bin/${source_fn}" "${BIN_LOCATION}/${target_fn}" + + # for backwards compat with old-style ghcup symlink we need to clean these up + if [ -L "${BIN_LOCATION}/${target_fn}" ] ; then + edo rm "${BIN_LOCATION}/${target_fn}" + fi + + create_shim "${target_fn}" unset source_fn target_fn done + + # create alias symlinks # shellcheck disable=SC2046 edo ln $(optionv "-v") -sf runghc "${BIN_LOCATION}"/runhaskell # shellcheck disable=SC2046 edo ln $(optionv "-v") -sf haddock-ghc "${BIN_LOCATION}"/haddock + # set ghc version + if ${is_local} ; then + printf "%s" "${myghcver}" > "${PWD}/.ghcup.ghcver" || die "failed to set GHC version locally! Not enough permissions?" + else + printf "%s" "${myghcver}" > "${INSTALL_BASE}/.ghcup.ghcver" || die "failed to set GHC version globally! Not enough permissions?" + fi + status_message "Done, make sure \"${BIN_LOCATION}\" is in your PATH!" unset myghcver inst_location f @@ -1857,10 +1932,13 @@ while [ $# -gt 0 ] ; do fi break;; set) + IS_LOCAL=false shift 1 while [ $# -gt 0 ] ; do case $1 in -h|--help) set_usage;; + -l|--local) IS_LOCAL=true + shift 1 ;; *) GHC_VER=$1 break;; esac @@ -1871,13 +1949,13 @@ while [ $# -gt 0 ] ; do if [ -z "${_tool_ver}" ] ; then die "Could not find a recommended GHC version, please report a bug at ${BUG_URL}!" fi - set_ghc "${_tool_ver}" + set_ghc "${_tool_ver}" ${IS_LOCAL} else # could be a version or a tag, let's check if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then - set_ghc "${GHC_VER}" + set_ghc "${GHC_VER}" ${IS_LOCAL} elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then - set_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" + set_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" ${IS_LOCAL} else die "\"${GHC_VER}\" is not a known version or tag!" fi