haskell-language-server/bindist/wrapper.in
maralorn f6dc2064d1
wrapper.in: Require runtime ghc-pkgs to be an abi compatible superset of bootpkgs (#3214)
This still makes sure that ghc has been compiled with the same core
libraries as hls while it allows runtime environments where other
packages have been added to the ghc-pkg database.

This commit also adds that file to the sdist, so that distro
packagers can use it.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2022-09-28 13:22:21 +00:00

187 lines
6.1 KiB
Bash

#!/bin/sh
exedir="@@EXE_DIR@@"
executablename="@@EXE_NAME@@"
GHC_VERSION="@@GHC_VERSION@@"
# This space separated list contains the names and versions of the boot libraries used to compile hls.
BOOT_PKGS="@@BOOT_PKGS@@"
# This space separated list contains the ABI hashes of the pkgs in BOOT_PKGS at compiletime.
ABI_HASHES="@@ABI_HASHES@@"
debug_msg() {
if [ -n "$HLS_WRAPPER_DEBUG" ] ; then
(>&2 printf "\\033[0;34m%s\\033[0m\\n" "$1")
fi
}
err_msg() {
if [ -n "$HLS_WRAPPER_DEBUG" ] ; then
(>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1")
elif [ -n "$HLS_WRAPPER_VERBOSE" ] ; then
(>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1")
fi
}
instruction_msg() {
(>&2 printf "\\033[0;35m%s\\033[0m\\n" "$1")
}
err_exit() {
msg="Couldn't find a working/matching GHC installation. Consider installing ghc-${GHC_VERSION} via ghcup or build HLS from source."
# adjust Content-Length when changing json
json="{\"jsonrpc\":\"2.0\", \"method\":\"window/showMessage\", \"params\": {\"type\": 1, \"message\": \"${msg}\"}}"
printf "%s\r\n" "Content-Length: 203"
printf "%s\r\n"
printf "%s" "${json}"
unset msg json
}
err_ghc_pkg() {
err_msg "Could not find a ghc-pkg binary (found: $1)!"
}
err_abi() {
err_msg "GHC ABIs don't match!"
err_msg ""
err_msg "Expected: ${ABI_HASHES}"
err_msg "Got: $1"
}
err_ver() {
err_msg "GHC versions don't match!"
err_msg ""
err_msg "Expected: ${GHC_VERSION}"
err_msg "Got: $1"
}
# Check the version of GHC and the ABI.
check_ghc() {
{ [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] ;} && debug_msg "internal error: not enough arguments to check_ghc: 1:$1,2:$2,3:$3" && return 4
check_ghc_libdir=$1
check_ghc_bin=$2
GHC_PKG=$3
check_ghc_ver="$("${check_ghc_bin}" --numeric-version 2>/dev/null)"
# check version
if [ "${check_ghc_ver}" = "${GHC_VERSION}" ] ; then
# check for all packages listed in BOOT_PKGS that they are present with the same ABI hash as at hls-compiletime to prevent linking issues.
if "${GHC_PKG}" --version >/dev/null ; then
:
elif "${GHC_PKG}-${GHC_VERSION}" --version >/dev/null ; then
GHC_PKG=${GHC_PKG}-${GHC_VERSION}
else
err_ghc_pkg "${GHC_PKG}"
unset GHC_LIBDIR
return 1
fi
PKGCONF="${check_ghc_libdir}/package.conf.d"
MY_ABI_HASHES="$(for dep in ${BOOT_PKGS} ; do printf "%s:" "${dep}" && "${GHC_PKG}" --global --global-package-db "$PKGCONF" field "${dep}" abi --simple-output ; done | tr '\n' ' ' | xargs)"
if [ "${ABI_HASHES}" != "${MY_ABI_HASHES}" ] ; then
err_abi "${MY_ABI_HASHES}"
return 3
fi
unset PKGCONF
else
err_ver "${check_ghc_ver}"
unset GHC_LIBDIR
return 2
fi
unset check_ghc_libdir check_ghc_bindir GHC_PKG check_ghc_ver
}
# Infer ghc-pkg from the given ghc path. Doesn't check for existence of any
# components.
infer_ghc_pkg() {
infer_ghc_path=$1
infer_ghc_bin=${infer_ghc_path##**/}
infer_ghc_ver_suffix=${infer_ghc_bin#ghc}
path_prefix="$(dirname "${infer_ghc_path}")"
if [ "${path_prefix}" = "." ] ; then
echo "ghc-pkg${infer_ghc_ver_suffix}"
elif [ "${path_prefix}" = "/" ] ; then
echo "${path_prefix}ghc-pkg${infer_ghc_ver_suffix}"
else
echo "${path_prefix}/ghc-pkg${infer_ghc_ver_suffix}"
fi
unset infer_ghc_path infer_ghc_bin infer_ghc_ver_suffix path_prefix
}
# try GHC_LIBDIR from the environment (e.g. user set it, or haskell-language-server-wrapper)
if [ -n "${GHC_LIBDIR}" ] &&
[ -n "${GHC_BIN}" ] &&
{ debug_msg "Trying method: GHC_LIBDIR and GHC_BIN from env" ; HLS_WRAPPER_VERBOSE=1 ; check_ghc "${GHC_LIBDIR}" "${GHC_BIN}" "$(infer_ghc_pkg "${GHC_BIN}")" || { err_exit ; exit 1 ; } ; }
then
:
# try GHC_BIN from the environment (e.g. user set it)
elif [ -n "${GHC_BIN}" ] &&
GHC_LIBDIR="$("${GHC_BIN}" --print-libdir)" &&
{ debug_msg "Trying method: GHC_BIN from env" ; HLS_WRAPPER_VERBOSE=1 ; check_ghc "${GHC_LIBDIR}" "${GHC_BIN}" "$(infer_ghc_pkg "${GHC_BIN}")" || { err_exit ; exit 2 ; } ; }
then
:
# try ghcup
elif command -v ghcup >/dev/null &&
GHC_BIN="$(ghcup whereis ghc "${GHC_VERSION}")" &&
GHC_LIBDIR="$("${GHC_BIN}" --print-libdir)" &&
{ debug_msg "Trying method: ghcup" ; check_ghc "${GHC_LIBDIR}" "${GHC_BIN}" "$(infer_ghc_pkg "${GHC_BIN}")" ; }
then
:
# try ghc-${GHC_VERSION}
elif command -v ghc-${GHC_VERSION} >/dev/null &&
GHC_LIBDIR="$("ghc-${GHC_VERSION}" --print-libdir)" &&
{ debug_msg "Trying method: ghc-${GHC_VERSION} in PATH" ; check_ghc "${GHC_LIBDIR}" "ghc-${GHC_VERSION}" "$(infer_ghc_pkg "ghc-${GHC_VERSION}")" ; }
then
:
# try ghc
elif command -v ghc >/dev/null &&
GHC_LIBDIR="$(ghc --print-libdir)" &&
{ debug_msg "Trying method: ghc in PATH" ; check_ghc "${GHC_LIBDIR}" "ghc" "$(infer_ghc_pkg "ghc")" ; }
then
:
# try stack
elif command -v stack >/dev/null &&
GHC_BIN="$(cd "$(mktemp -d)" && stack --no-system-ghc --no-install-ghc --resolver "ghc-${GHC_VERSION}" exec sh -- -c 'command -v ghc')" &&
GHC_LIBDIR="$("${GHC_BIN}" --print-libdir)" &&
{ debug_msg "Trying method: stack" ; check_ghc "${GHC_LIBDIR}" "${GHC_BIN}" "$(infer_ghc_pkg "${GHC_BIN}")" ; }
then
:
else
HLS_WRAPPER_VERBOSE=1
err_msg "All methods exhausted!"
err_exit
err_msg "exiting..."
exit 42
fi
debug_msg "Found GHC libdir at: ${GHC_LIBDIR}"
case "$(uname -s)" in
"Darwin"|"darwin")
if [ -n "$DYLD_LIBRARY_PATH" ] ; then
DYLD_LIBRARY_PATH="$(for i in "${GHC_LIBDIR}"/* ; do [ -d "$i" ] && printf "%s" "$i:" ; done)$DYLD_LIBRARY_PATH"
debug_msg "Exporting DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}"
export DYLD_LIBRARY_PATH
else
DYLD_LIBRARY_PATH="$(for i in "${GHC_LIBDIR}"/* ; do [ -d "$i" ] && printf "%s" "$i:" ; done | sed 's/:$//')"
debug_msg "Exporting DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}"
export DYLD_LIBRARY_PATH
fi
;;
*)
if [ -n "$LD_LIBRARY_PATH" ] ; then
LD_LIBRARY_PATH="$(for i in "${GHC_LIBDIR}"/* ; do [ -d "$i" ] && printf "%s" "$i:" ; done)$LD_LIBRARY_PATH"
debug_msg "Exporting LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
else
LD_LIBRARY_PATH="$(for i in "${GHC_LIBDIR}"/* ; do [ -d "$i" ] && printf "%s" "$i:" ; done | sed 's/:$//')"
debug_msg "Exporting LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
fi
;;
esac
exec "${exedir}/${executablename}" ${1+"$@"}