#!/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+"$@"}