ghcup/ghcup.sh

505 lines
14 KiB
Bash
Raw Normal View History

2018-09-29 07:50:26 +00:00
#!/bin/sh
2018-09-29 15:31:07 +00:00
#
# Copyright (c) 2018, Julian Ospald <hasufell@posteo.de>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the <ORGANIZATION> nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
2018-09-29 07:50:26 +00:00
2018-09-29 07:50:26 +00:00
## global variables ##
VERSION=0.0.1
2018-09-29 13:46:39 +00:00
SCRIPT="$(basename "$0")"
2018-09-29 07:50:26 +00:00
VERBOSE=false
FORCE=false
INSTALL_BASE="$HOME/.ghcup"
2018-09-29 07:50:26 +00:00
## print help ##
usage() {
2018-09-29 13:03:24 +00:00
(>&2 echo "ghcup ${VERSION}
GHC up toolchain installer
USAGE:
${SCRIPT} [FLAGS] <SUBCOMMAND>
2018-09-29 07:50:26 +00:00
FLAGS:
-v, --verbose Enable verbose output
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
install Install GHC
2018-09-29 15:01:39 +00:00
show Show current/installed GHC
2018-09-29 13:07:57 +00:00
set Set currently active GHC version
self-update Update this script in-place
2018-09-29 07:50:26 +00:00
")
exit 1
}
install_usage() {
2018-09-29 13:03:24 +00:00
(>&2 echo "ghcup-install
Install the specified GHC version
USAGE:
${SCRIPT} install [FLAGS] <VERSION>
2018-09-29 07:50:26 +00:00
FLAGS:
-h, --help Prints help information
-f, --force Overwrite already existing installation
2018-09-29 07:50:26 +00:00
ARGS:
<VERSION> E.g. \"8.4.3\" or \"8.6.1\"
")
exit 1
}
2018-09-29 13:07:57 +00:00
set_usage() {
(>&2 echo "ghcup-set
2018-09-29 13:03:24 +00:00
Set the currently active GHC to the specified version
USAGE:
2018-09-29 13:07:57 +00:00
${SCRIPT} set [FLAGS] <VERSION>
FLAGS:
-h, --help Prints help information
ARGS:
<VERSION> E.g. \"8.4.3\" or \"8.6.1\"
")
exit 1
}
self_update_usage() {
2018-09-29 13:03:24 +00:00
(>&2 echo "ghcup-self-update
Update the ghcup.sh script in-place
USAGE:
${SCRIPT} self-update [FLAGS] [TARGET-LOCATION]
FLAGS:
-h, --help Prints help information
ARGS:
[TARGET-LOCATION] Where to place the updated script (defaults to ~/.local/bin).
Must be an absolute path!
")
exit 1
}
2018-09-29 07:50:26 +00:00
2018-09-29 15:01:39 +00:00
show_usage() {
(>&2 echo "ghcup-show
Show the installed/current GHC versions
USAGE:
${SCRIPT} show [FLAGS]
FLAGS:
-h, --help Prints help information
-i, --installed Show installed GHC version only
")
exit 1
}
2018-09-29 07:50:26 +00:00
## utilities ##
die() {
(>&2 echo "$1")
exit 2
}
2018-09-29 14:31:08 +00:00
edo()
{
if ${VERBOSE} ; then
echo "$@" 1>&2
fi
"$@" || exit 2
}
echov() {
2018-09-29 07:50:26 +00:00
if ${VERBOSE} ; then
echo "$1"
2018-09-29 07:50:26 +00:00
else
if [ -n "$2" ] ; then
echov "$2"
fi
2018-09-29 07:50:26 +00:00
fi
}
printf_green() {
2018-09-29 13:46:39 +00:00
printf "\\033[0;32m%s\\033[0m\\n" "$1"
2018-09-29 07:50:26 +00:00
}
get_distro_name() {
if [ -f /etc/os-release ]; then
# freedesktop.org and systemd
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC1091
2018-09-29 07:50:26 +00:00
. /etc/os-release
2018-09-29 13:46:39 +00:00
printf "%s" "$NAME"
2018-09-29 09:53:14 +00:00
elif command -V lsb_release >/dev/null 2>&1; then
2018-09-29 07:50:26 +00:00
# linuxbase.org
2018-09-29 13:46:39 +00:00
printf "%s" "$(lsb_release -si)"
2018-09-29 07:50:26 +00:00
elif [ -f /etc/lsb-release ]; then
# For some versions of Debian/Ubuntu without lsb_release command
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC1091
2018-09-29 07:50:26 +00:00
. /etc/lsb-release
2018-09-29 13:46:39 +00:00
printf "%s" "$DISTRIB_ID"
2018-09-29 07:50:26 +00:00
elif [ -f /etc/debian_version ]; then
# Older Debian/Ubuntu/etc.
printf "Debian"
else
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
2018-09-29 13:46:39 +00:00
printf "%s" "$(uname -s)"
2018-09-29 07:50:26 +00:00
fi
}
get_distro_ver() {
if [ -f /etc/os-release ]; then
# freedesktop.org and systemd
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC1091
2018-09-29 07:50:26 +00:00
. /etc/os-release
2018-09-29 13:46:39 +00:00
printf "%s" "$VERSION_ID"
2018-09-29 09:53:14 +00:00
elif command -V lsb_release >/dev/null 2>&1; then
2018-09-29 07:50:26 +00:00
# linuxbase.org
2018-09-29 13:46:39 +00:00
printf "%s" "$(lsb_release -sr)"
2018-09-29 07:50:26 +00:00
elif [ -f /etc/lsb-release ]; then
# For some versions of Debian/Ubuntu without lsb_release command
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC1091
2018-09-29 07:50:26 +00:00
. /etc/lsb-release
2018-09-29 13:46:39 +00:00
printf "%s" "$DISTRIB_RELEASE"
2018-09-29 07:50:26 +00:00
elif [ -f /etc/debian_version ]; then
# Older Debian/Ubuntu/etc.
2018-09-29 13:46:39 +00:00
printf "%s" "$(cat /etc/debian_version)"
2018-09-29 07:50:26 +00:00
else
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
2018-09-29 13:46:39 +00:00
printf "%s" "$(uname -r)"
2018-09-29 07:50:26 +00:00
fi
}
get_arch() {
myarch=$(uname -m)
case "${myarch}" in
x86_64)
printf "x86_64" # or AMD64 or Intel64 or whatever
;;
i*86)
printf "i386" # or IA32 or Intel32 or whatever
;;
*)
die "Cannot figure out architecture (was: ${myarch})"
;;
esac
unset myarch
}
get_download_url() {
myghcver=$1
myarch=$(get_arch)
mydistro=$(get_distro_name)
mydistrover=$(get_distro_ver)
baseurl="https://downloads.haskell.org/~ghc"
# TODO: awkward, restructure
2018-09-29 07:50:26 +00:00
case "${mydistro},${mydistrover},${myarch},${myghcver}" in
Debian,7,i386,8.2.2)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb${mydistrover}-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
*,*,i386,*)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb8-linux.tar.xz"
;;
Debian,*,*,8.2.2)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb8-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
Debian,8,*,*)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb8-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
Debian,*,*,*)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb9-linux.tar.xz"
;;
Ubuntu,*,*,8.2.2)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb8-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
Ubuntu,*,*,*)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb9-linux.tar.xz"
;;
*,*,*,8.2.2)
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-deb8-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
*,*,*,*) # this is our best guess
2018-09-29 13:46:39 +00:00
printf "%s" "${baseurl}/${myghcver}/ghc-${myghcver}-${myarch}-fedora27-linux.tar.xz"
;;
2018-09-29 07:50:26 +00:00
esac
unset myghcver myarch mydistro mydistrover baseurl
}
## subcommand install ##
install_ghc() {
myghcver=$1
downloader=curl
2018-09-29 09:27:01 +00:00
downloader_opts="--fail -O"
inst_location=${INSTALL_BASE}/ghc/${myghcver}
2018-09-29 09:27:01 +00:00
target_location=${INSTALL_BASE}/bin
2018-09-29 15:12:13 +00:00
download_url=$(get_download_url "${myghcver}")
download_tarball_name=$(basename "${download_url}")
2018-09-29 09:27:01 +00:00
[ -e "${target_location}" ] || mkdir "${target_location}"
if [ -e "${inst_location}" ] ; then
if ${FORCE} ; then
echo "GHC already installed in ${inst_location}, overwriting!"
else
die "GHC already installed in ${inst_location}, use --force to overwrite"
fi
fi
2018-09-29 07:50:26 +00:00
printf_green "Installing GHC for $(get_distro_name) on architecture $(get_arch)"
2018-09-29 14:31:08 +00:00
tmp_dir=$(mktemp -d)
[ -z "${tmp_dir}" ] && die "Failed to create temporary directory"
(
2018-09-29 14:31:08 +00:00
edo cd "${tmp_dir}"
2018-09-29 07:50:26 +00:00
2018-09-29 15:12:13 +00:00
echov "Downloading ${download_url}"
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2086
2018-09-29 15:12:13 +00:00
edo ${downloader} ${downloader_opts} "${download_url}"
2018-09-29 07:50:26 +00:00
2018-09-29 14:31:08 +00:00
edo tar -xf ghc-*-linux.tar.xz
edo cd "ghc-${myghcver}"
2018-09-29 07:50:26 +00:00
echov "Installing GHC into ${inst_location}"
2018-09-29 14:31:08 +00:00
edo ./configure --prefix="${inst_location}"
edo make install
2018-09-29 09:35:39 +00:00
# clean up
2018-09-29 14:31:08 +00:00
edo cd ..
2018-09-29 15:12:13 +00:00
[ -e "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
[ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
2018-09-29 14:31:08 +00:00
) || {
2018-09-29 15:12:13 +00:00
[ -e "${tmp_dir}/${download_tarball_name}" ] && rm "${tmp_dir}/${download_tarball_name}"
[ -e "${tmp_dir}/ghc-${myghcver}" ] && rm -r "${tmp_dir}/ghc-${myghcver}"
die "Failed to install"
2018-09-29 14:31:08 +00:00
}
2018-09-29 13:46:39 +00:00
for f in "${inst_location}"/bin/*-"${myghcver}" ; do
2018-09-29 15:02:00 +00:00
[ -e "${f}" ] || die "Something went wrong, ${f} does not exist!"
2018-09-29 13:46:39 +00:00
fn=$(basename "${f}")
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2046
edo ln $(echov "-v") -sf ../ghc/"${myghcver}/bin/${fn}" "${target_location}/${fn}"
2018-09-29 09:27:01 +00:00
unset fn
done
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2046
edo ln $(echov "-v") -sf ../ghc/"${myghcver}"/bin/runhaskell "${target_location}/runhaskell-${myghcver}"
2018-09-29 09:27:01 +00:00
printf_green "Done installing, run \"ghci-${myghcver}\" or set up your current GHC via: ${SCRIPT} set-ghc ${myghcver}"
2018-09-29 15:12:13 +00:00
unset myghcver downloader downloader_opts inst_location target_location f download_url download_tarball_name
}
## subcommand set-ghc ##
set_ghc() {
myghcver=$1
target_location=${INSTALL_BASE}/bin
inst_location=${INSTALL_BASE}/ghc/${myghcver}
[ -e "${inst_location}" ] || die "GHC ${myghcver} not installed yet, use: ${SCRIPT} install ${myghcver}"
2018-09-29 14:31:08 +00:00
[ -e "${target_location}" ] || edo mkdir "${target_location}"
printf_green "Setting GHC to ${myghcver}"
2018-09-29 13:46:39 +00:00
for f in "${inst_location}"/bin/*-"${myghcver}" ; do
2018-09-29 15:02:00 +00:00
[ -e "${f}" ] || die "Something went wrong, ${f} does not exist!"
2018-09-29 13:46:39 +00:00
source_fn=$(basename "${f}")
target_fn=$(echo "${source_fn}" | sed "s#-${myghcver}##")
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2046
edo ln $(echov "-v") -sf ../ghc/"${myghcver}/bin/${source_fn}" "${target_location}/${target_fn}"
unset source_fn target_fn
done
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2046
2018-09-29 14:31:08 +00:00
edo ln $(echov "-v") -sf runghc "${target_location}"/runhaskell
2018-09-29 07:50:26 +00:00
printf_green "Done, make sure \"${target_location}\" is in your PATH!"
2018-09-29 07:50:26 +00:00
2018-09-29 09:27:01 +00:00
unset myghcver target_location inst_location f
2018-09-29 07:50:26 +00:00
}
## self-update subcommand ##
self_update() {
target_location=$1
source_url="https://raw.githubusercontent.com/hasufell/ghcup/master/ghcup.sh"
downloader=curl
2018-09-29 09:27:01 +00:00
downloader_opts="--fail -O"
[ -e "${target_location}" ] || die "Destination \"${target_location}\" does not exist, cannot update script"
printf_green "Updating ${SCRIPT}"
(
2018-09-29 14:31:08 +00:00
edo cd "$(mktemp -d)"
echov "Downloading ${source_url}"
2018-09-29 14:22:54 +00:00
# shellcheck disable=SC2086
2018-09-29 14:31:08 +00:00
edo ${downloader} ${downloader_opts} "${source_url}"
edo mv ghcup.sh "${target_location}"/ghcup.sh
edo chmod +x "${target_location}"/ghcup.sh
)
printf_green "Done, make sure \"${target_location}\" is in your PATH!"
unset target_location source_url downloader downloader_opts
}
2018-09-29 15:01:39 +00:00
## show subcommand ##
show_ghc() {
ghc_location=${INSTALL_BASE}/ghc
current_ghc=$(show_ghc_installed)
echo "Installed GHCs:"
for i in "${ghc_location}"/* ; do
[ -e "${i}" ] || die "Something went wrong, ${i} does not exist!"
echo " $(basename "${i}")"
done
if [ -n "${current_ghc}" ] ; then
echo
echo "Current GHC"
echo " ${current_ghc}"
fi
unset target_location i
}
show_ghc_installed() {
target_location=${INSTALL_BASE}/bin
real_ghc=$(realpath "${target_location}/ghc")
if [ -e "${real_ghc}" ] ; then
real_ghc="$(basename "${real_ghc}" | sed 's#ghc-##')"
printf "%s" "${real_ghc}"
fi
unset target_location real_ghc
}
2018-09-29 07:50:26 +00:00
## command line parsing and entry point ##
# sanity checks
if [ -z "$HOME" ] ; then
die "HOME env not set, cannot operate"
fi
[ $# -lt 1 ] && usage
2018-09-29 07:50:26 +00:00
while [ $# -gt 0 ] ; do
case $1 in
-v|--verbose)
VERBOSE=true
shift 1;;
-V|--version)
2018-09-29 13:46:39 +00:00
printf "%s" "${VERSION}"
2018-09-29 07:50:26 +00:00
exit 0;;
-h|--help)
usage;;
*) case $1 in
install)
shift 1
while [ $# -gt 0 ] ; do
case $1 in
-h|--help) install_usage;;
-f|--force) FORCE=true
shift 1;;
2018-09-29 07:50:26 +00:00
*) GHC_VER=$1
break;;
esac
done
[ "${GHC_VER}" ] || install_usage
2018-09-29 13:46:39 +00:00
install_ghc "${GHC_VER}"
2018-09-29 07:50:26 +00:00
break;;
2018-09-29 13:07:57 +00:00
set)
shift 1
while [ $# -gt 0 ] ; do
case $1 in
2018-09-29 13:07:57 +00:00
-h|--help) set_usage;;
*) GHC_VER=$1
break;;
esac
done
2018-09-29 13:07:57 +00:00
[ "${GHC_VER}" ] || set_usage
2018-09-29 13:46:39 +00:00
set_ghc "${GHC_VER}"
break;;
self-update)
shift 1
while [ $# -gt 0 ] ; do
case $1 in
-h|--help) self_update_usage;;
*) TARGET_LOCATION=$1
break;;
esac
done
if [ "${TARGET_LOCATION}" ] ; then
self_update "${TARGET_LOCATION}"
else
self_update "${HOME}/.local/bin"
fi
break;;
2018-09-29 15:01:39 +00:00
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;;
2018-09-29 07:50:26 +00:00
*) usage;;
esac
break;;
esac
done