diff --git a/README.md b/README.md index 6ca122f..4a88a61 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,24 @@ Similar in scope to [rustup](https://github.com/rust-lang-nursery/rustup.rs), [p ## Installation +### Simple bootstrap of ghcup, GHC and cabal-install + +```sh +# complete bootstrap +curl https://raw.githubusercontent.com/haskell/ghcup/master/bootstrap-haskell -sSf | sh + +# prepare your environment +. "$HOME/.ghcup/env" +echo '. $HOME/.ghcup/env' >> "$HOME/.bashrc" # or similar + +# now create a project, such as: +mkdir myproject && cd myproject +cabal init -n --is-executable +cabal v2-run +``` + +### Manual install + Just place the `ghcup` shell script into your `PATH` anywhere. E.g.: diff --git a/bootstrap-haskell b/bootstrap-haskell new file mode 100755 index 0000000..5b2b026 --- /dev/null +++ b/bootstrap-haskell @@ -0,0 +1,75 @@ +#!/bin/sh + +# safety subshell to avoid executing anything in case this script is not downloaded properly +( + +die() { + (>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1") + exit 2 +} + +edo() +{ + printf "\\033[0;35m%s\\033[0m\\n" "$*" + "$@" || exit 2 +} + +echo +echo "Welcome to Haskell!" +echo +echo "This will download and install the Glasgow Haskell Compiler (GHC) for " +echo "the Haskell programming language, and the Cabal build tool." +echo +echo "It will add the 'cabal', 'ghc', and 'ghcup' executables to bin directory " +echo "located at: " +echo +echo " $HOME/.ghcup/bin" +echo +echo "and create the environment file $HOME/.ghcup/env" +echo "which you should source in your ~/.bashrc or similar to get the required" +echo "PATH components." +echo + +if [ -z "${BOOTSTRAP_HASKELL_NONINTERACTIVE}" ] ; then + echo "To proceed with the installation press enter, to cancel press ctrl-c." + echo "Note that this script can be re-run at any given time." + echo + + # Wait for user input to continue. + # shellcheck disable=SC2034 + read -r answer /dev/null 2>&1 ; then + if [ -z "${BOOTSTRAP_HASKELL_NO_UPGRADE}" ] ; then + edo ghcup upgrade + fi +else + edo curl https://raw.githubusercontent.com/haskell/ghcup/master/ghcup > "${HOME}"/.ghcup/bin/ghcup + edo chmod +x "${HOME}"/.ghcup/bin/ghcup + + cat <<-EOF > "${HOME}"/.ghcup/env || die "Failed to create env file" + export PATH="\$HOME/.cabal/bin:\$HOME/.ghcup/bin:\$PATH" + EOF + # shellcheck disable=SC1090 + edo . "${HOME}"/.ghcup/env +fi + +edo ghcup install + +edo ghcup set +edo ghcup install-cabal + +edo cabal new-update +edo cabal new-install --symlink-bindir="$HOME/.cabal/bin" cabal-install + +printf "\\033[0;35m%s\\033[0m\\n" "" +printf "\\033[0;35m%s\\033[0m\\n" "Installation done!" +printf "\\033[0;35m%s\\033[0m\\n" "" +printf "\\033[0;35m%s\\033[0m\\n" "Don't forget to source $HOME/.ghcup/env in your ~/.bashrc or similar." +printf "\\033[0;35m%s\\033[0m\\n" "" + + +) diff --git a/ghcup b/ghcup index 623c530..3bd30ad 100755 --- a/ghcup +++ b/ghcup @@ -239,13 +239,15 @@ set_usage() { Set the currently active GHC to the specified version USAGE: - ${SCRIPT} set [FLAGS] + ${SCRIPT} set [FLAGS] [VERSION|TAG] FLAGS: -h, --help Prints help information ARGS: - E.g. \"8.4.3\" or \"8.6.1\" + [VERSION|TAG] E.g. \"8.4.3\" or \"8.6.3\" or + a tag like \"recommended\" or \"latest\" + (default: discovers recommended version) DISCUSSION: Sets the the current GHC version by creating non-versioned @@ -1004,34 +1006,6 @@ posix_realpath() { unset mysource mydir posix_realpath_error } -# @FUNCTION: is_sourced -# @DESCRIPTION: -# Tries to figure out if we are being sourced. Based on -# https://stackoverflow.com/a/28776166 -# @RETURNS: 0 if we are being sourced, 1 otherwise -is_sourced() { - if [ -n "$ZSH_EVAL_CONTEXT" ]; then - case $ZSH_EVAL_CONTEXT in - *:file) - return 0 ;; - esac - elif [ -n "$KSH_VERSION" ]; then - # shellcheck disable=SC2154 - [ "$(cd "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && return 0 - elif [ -n "$BASH_VERSION" ]; then - # shellcheck disable=SC2128 - [ "$0" != "$BASH_SOURCE" ] && return 0 - else # All other shells: examine $0 for known shell binary filenames - # Detects `sh` and `dash`; add additional shell filenames as needed. - case ${0##*/} in - sh|dash) - return 0 ;; - esac - fi - - # assume we are not sourced, if our above checks didn't find it - return 1 -} # @FUNCTION: get_meta_version_file # @DESCRIPTION: @@ -1188,7 +1162,8 @@ install_ghc() { if ${FORCE} ; then echo "GHC already installed in ${inst_location}, overwriting!" else - die "GHC already installed in ${inst_location}, use --force to overwrite" + warning_message "GHC already installed in ${inst_location}, use --force to overwrite" + exit 0 fi first_install=false fi @@ -1693,222 +1668,235 @@ fi #--[ Command line parsing and entry point ]--# ############################################## -if ! is_sourced ; then +[ $# -lt 1 ] && usage - [ $# -lt 1 ] && usage +while [ $# -gt 0 ] ; do + case $1 in + -v|--verbose) + VERBOSE=true + shift 1 + if [ $# -lt 1 ] ; then + usage + fi + ;; + -V|--version) + printf "%s" "${VERSION}" + exit 0;; + -h|--help) + usage;; + -w|--wget) + DOWNLOADER="wget" + DOWNLOADER_OPTS="" + shift 1 + if [ $# -lt 1 ] ; then + usage + fi + ;; + -c|--cache) + CACHING=true + shift 1 + if [ $# -lt 1 ] ; then + usage + fi + ;; + *) ## startup tasks ## - while [ $# -gt 0 ] ; do - case $1 in - -v|--verbose) - VERBOSE=true - shift 1 - if [ $# -lt 1 ] ; then - usage - fi - ;; - -V|--version) - printf "%s" "${VERSION}" - exit 0;; - -h|--help) - usage;; - -w|--wget) - DOWNLOADER="wget" - DOWNLOADER_OPTS="" - shift 1 - if [ $# -lt 1 ] ; then - usage - fi - ;; - -c|--cache) - CACHING=true - shift 1 - if [ $# -lt 1 ] ; then - usage - fi - ;; - *) ## startup tasks ## + edo mkdir -p "${INSTALL_BASE}" + edo mkdir -p "${BIN_LOCATION}" + edo mkdir -p "${CACHE_LOCATION}" - edo mkdir -p "${INSTALL_BASE}" - edo mkdir -p "${BIN_LOCATION}" - edo mkdir -p "${CACHE_LOCATION}" + # clean up old meta files + if [ -e "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")" ] ; then + edo rm "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")" + fi + if [ -e "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")" ] ; then + edo rm "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")" + fi - # clean up old meta files - if [ -e "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")" ] ; then - edo rm "${CACHE_LOCATION}/$(basename "${META_VERSION_URL}")" + # check for available commands + missing_commands="$(check_required_commands ${DOWNLOADER})" + if [ -n "${missing_commands}" ] ; then + die "Following commands are required, but missing, please install: ${missing_commands}" + fi + unset missing_commands + case $1 in + install) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) install_usage;; + -f|--force) FORCE=true + shift 1;; + *) GHC_VER=$1 + break;; + esac + done + if [ -z "${GHC_VER}" ] ; then + _tool_ver="$(get_tool_ver_from_tag "ghc" "recommended")" + if [ -z "${_tool_ver}" ] ; then + die "Could not find a recommended GHC version, please report a bug at ${BUG_URL}!" + fi + install_ghc "${_tool_ver}" + else + # could be a version or a tag, let's check + if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then + install_ghc "${GHC_VER}" + elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then + install_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" + else + die "\"${GHC_VER}\" is not a known version or tag!" + fi fi - if [ -e "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")" ] ; then - edo rm "${CACHE_LOCATION}/$(basename "${META_DOWNLOAD_URL}")" - fi - - # check for available commands - missing_commands="$(check_required_commands ${DOWNLOADER})" - if [ -n "${missing_commands}" ] ; then - die "Following commands are required, but missing, please install: ${missing_commands}" - fi - unset missing_commands - case $1 in - install) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) install_usage;; - -f|--force) FORCE=true - shift 1;; - *) GHC_VER=$1 - break;; - esac - done - if [ -z "${GHC_VER}" ] ; then - _tool_ver="$(get_tool_ver_from_tag "ghc" "recommended")" - if [ -z "${_tool_ver}" ] ; then - die "Could not find a recommended GHC version, please report a bug at ${BUG_URL}!" - fi - install_ghc "${_tool_ver}" - else - # could be a version or a tag, let's check - if array_contains "${GHC_VER}" "$(known_tool_versions "ghc")" ; then - install_ghc "${GHC_VER}" - elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then - install_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" - else - die "\"${GHC_VER}\" is not a known version or tag!" - fi - fi - break;; - set) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) set_usage;; - *) GHC_VER=$1 - break;; - esac - done - [ -n "${GHC_VER}" ] || set_usage - set_ghc "${GHC_VER}" - break;; - upgrade) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) upgrade_usage;; - *) TARGET_LOCATION=$1 - break;; - esac - done - if [ -n "${TARGET_LOCATION}" ] ; then - upgrade "${TARGET_LOCATION}" - else - upgrade "$(dirname "$(posix_realpath "${SOURCE}")")" - fi - break;; - show) - SHOW_INSTALLED=false - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) show_usage;; - -i|--installed) SHOW_INSTALLED=true - break;; - *) show_usage;; - esac - done - if ${SHOW_INSTALLED} ; then - show_ghc_installed - else - show_ghc - fi - break;; - rm) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) rm_usage;; - -f|--force) FORCE=true - shift 1;; - *) GHC_VER=$1 - break;; - esac - done - [ -n "${GHC_VER}" ] || rm_usage - rm_ghc "${GHC_VER}" - break;; - install-cabal) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) install_cabal_usage;; - -f|--force) FORCE=true - shift 1;; - *) CABAL_VER=$1 - break;; - esac - done - if [ -n "${CABAL_VER}" ] ; then - # could be a version or a tag, let's check - if array_contains "${CABAL_VER}" "$(known_tool_versions "cabal-install")" ; then - install_cabal "${CABAL_VER}" - elif array_contains "${CABAL_VER}" "$(known_tool_tags "cabal-install")" ; then - install_cabal "$(get_tool_ver_from_tag "cabal-install" "${CABAL_VER}")" - else - die "\"${CABAL_VER}\" is not a known version or tag!" - fi - else - _cabal_ver="$(get_tool_ver_from_tag "cabal-install" "recommended")" - if [ -z "${_cabal_ver}" ] ; then - die "Could not find a recommended cabal-install version, please report a bug at ${BUG_URL}!" - fi - install_cabal "${_cabal_ver}" - fi - break;; - compile) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) compile_usage;; - -f|--force) FORCE=true - shift 1;; - -j|--jobs) JOBS=$2 - shift 2;; - -c|--build-config) BUILD_CONFIG=$2 - shift 2;; - *) GHC_VER=$1 - BOOTSTRAP_GHC=$2 - break;; - esac - done - [ -n "${GHC_VER}" ] || compile_usage - [ -n "${BOOTSTRAP_GHC}" ] || compile_usage - compile_ghc "${GHC_VER}" "${BOOTSTRAP_GHC}" "${BUILD_CONFIG}" - break;; - debug-info) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) debug_info_usage;; - *) debug_info_usage;; - esac - done - print_debug_info - break;; - list) - shift 1 - while [ $# -gt 0 ] ; do - case $1 in - -h|--help) list_usage;; - -t|--tool) TOOL=$2 - shift 2;; - *) list_usage;; - esac - done - list "${TOOL}" - break;; - *) usage;; - esac break;; - esac - done -fi # is_sourced + set) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) set_usage;; + *) GHC_VER=$1 + break;; + esac + done + + if [ -z "${GHC_VER}" ] ; then + _tool_ver="$(get_tool_ver_from_tag "ghc" "recommended")" + 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}" + 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}" + elif array_contains "${GHC_VER}" "$(known_tool_tags "ghc")" ; then + set_ghc "$(get_tool_ver_from_tag "ghc" "${GHC_VER}")" + else + die "\"${GHC_VER}\" is not a known version or tag!" + fi + fi + + break;; + upgrade) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) upgrade_usage;; + *) TARGET_LOCATION=$1 + break;; + esac + done + if [ -n "${TARGET_LOCATION}" ] ; then + upgrade "${TARGET_LOCATION}" + else + upgrade "$(dirname "$(posix_realpath "${SOURCE}")")" + fi + break;; + show) + SHOW_INSTALLED=false + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) show_usage;; + -i|--installed) SHOW_INSTALLED=true + break;; + *) show_usage;; + esac + done + if ${SHOW_INSTALLED} ; then + show_ghc_installed + else + show_ghc + fi + break;; + rm) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) rm_usage;; + -f|--force) FORCE=true + shift 1;; + *) GHC_VER=$1 + break;; + esac + done + [ -n "${GHC_VER}" ] || rm_usage + rm_ghc "${GHC_VER}" + break;; + install-cabal) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) install_cabal_usage;; + -f|--force) FORCE=true + shift 1;; + *) CABAL_VER=$1 + break;; + esac + done + if [ -n "${CABAL_VER}" ] ; then + # could be a version or a tag, let's check + if array_contains "${CABAL_VER}" "$(known_tool_versions "cabal-install")" ; then + install_cabal "${CABAL_VER}" + elif array_contains "${CABAL_VER}" "$(known_tool_tags "cabal-install")" ; then + install_cabal "$(get_tool_ver_from_tag "cabal-install" "${CABAL_VER}")" + else + die "\"${CABAL_VER}\" is not a known version or tag!" + fi + else + _cabal_ver="$(get_tool_ver_from_tag "cabal-install" "recommended")" + if [ -z "${_cabal_ver}" ] ; then + die "Could not find a recommended cabal-install version, please report a bug at ${BUG_URL}!" + fi + install_cabal "${_cabal_ver}" + fi + break;; + compile) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) compile_usage;; + -f|--force) FORCE=true + shift 1;; + -j|--jobs) JOBS=$2 + shift 2;; + -c|--build-config) BUILD_CONFIG=$2 + shift 2;; + *) GHC_VER=$1 + BOOTSTRAP_GHC=$2 + break;; + esac + done + [ -n "${GHC_VER}" ] || compile_usage + [ -n "${BOOTSTRAP_GHC}" ] || compile_usage + compile_ghc "${GHC_VER}" "${BOOTSTRAP_GHC}" "${BUILD_CONFIG}" + break;; + debug-info) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) debug_info_usage;; + *) debug_info_usage;; + esac + done + print_debug_info + break;; + list) + shift 1 + while [ $# -gt 0 ] ; do + case $1 in + -h|--help) list_usage;; + -t|--tool) TOOL=$2 + shift 2;; + *) list_usage;; + esac + done + list "${TOOL}" + break;; + *) usage;; + esac + break;; + esac +done # vim: tabstop=4 shiftwidth=4 expandtab