#!/bin/sh # Copyright (c) 2016- Jade Allen # Copyright (c) 2011, 2012 Spawngrid, Inc # Copyright (c) 2011 Evax Software # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. unset ERL_TOP # Make sure CDPATH doesn't affect cd in case path is relative. unset CDPATH if [ -n "$KERL_DEBUG" ]; then set -x ## Line numbers do not work in dash (aka debian/ubuntu sh) ## so you may want to change sh to bash to get better debug info if [ -n "${LINENO}" ]; then PS4='+ ${LINENO}: ' fi fi KERL_VERSION='3.1.0' ERLANG_DOWNLOAD_URL='https://erlang.org/download' KERL_CONFIG_STORAGE_FILENAME='.kerl_config' # Redirect to stderr only if it is a terminal, otherwise mute stderr() { if [ -t 1 ] ; then if [ -z "$l" ] || [ "$KERL_COLORIZE" -eq 0 ]; then echo "$*" 1>&2 else left="$(colorize "$l")" # shellcheck disable=SC2086 right="$(tput $KERL_COLOR_RESET)" echo "${left}$*${right}" 1>&2 unset -v l fi fi } # Check if tput is available for colorize tp=$(tput sgr0 1> /dev/null 2>&1 && printf "OK" || printf "") if [ -z "$tp" ]; then stderr "Colorization disabled as 'tput' (via 'ncurses') seems to be unavailable" KERL_COLOR_AVAILABLE=0 KERL_COLORIZE=0 # Force disabling colorization else KERL_COLOR_AVAILABLE=1 if [ -n "$KERL_COLOR_D" ]; then KERL_COLOR_RESET="setaf $KERL_COLOR_D" else KERL_COLOR_RESET="sgr0" fi fi colorize() { case "$1" in "error"|"err"|"e") ret="$(tput setaf "${KERL_COLOR_E:=1}")" ;; "warning"|"warn"|"w") ret="$(tput setaf "${KERL_COLOR_W:=3}")" ;; "notice"|"note"|"n") ret="$(tput setaf "${KERL_COLOR_N:=4}")" ;; "tip"|"t") ret="$(tput setaf "${KERL_COLOR_T:=6}")" ;; "success"|"s") ret="$(tput setaf "${KERL_COLOR_S:=2}")" ;; *) ret="$(tput setaf "${KERL_COLOR_D:=9}")" esac printf "%b" "$ret" } autoclean() { if [ "${KERL_AUTOCLEAN:=1}" -eq 1 ]; then l=n stderr "Removing all artifacts except the logfile" l=t stderr "(Use KERL_AUTOCLEAN=0 to keep build on failure, if desired)" # Save LOGFILE tmplf="$(mktemp "$TMP_DIR"/kerl.XXXXXX)" test -f "$LOGFILE" && cp -f "$LOGFILE" "$tmplf" # Cleaning current build cd - 1>/dev/null 2>&1 || exit test -n "$1" && $0 cleanup "$1" # Copy LOGFILE back to keep track of all attempts until success mkdir -p "$(dirname "$LOGFILE")" mv -f "$tmplf" "$LOGFILE" unset tmplf else l=w stderr "Autoclean on failure disabled" fi } TMP_DIR=${TMP_DIR:-'/tmp'} if [ -z "$HOME" ]; then # shellcheck disable=SC2016 l=e stderr 'Error: $HOME is empty or not set.' exit 1 fi # Default values KERL_BASE_DIR=${KERL_BASE_DIR:="$HOME"/.kerl} KERL_CONFIG=${KERL_CONFIG:="$HOME"/.kerlrc} KERL_DOWNLOAD_DIR=${KERL_DOWNLOAD_DIR:="${KERL_BASE_DIR:?}"/archives} KERL_BUILD_DIR=${KERL_BUILD_DIR:="${KERL_BASE_DIR:?}"/builds} KERL_GIT_DIR=${KERL_GIT_DIR:="${KERL_BASE_DIR:?}"/gits} KERL_GIT_BASE=https://raw.githubusercontent.com/kerl/kerl/master KERL_COLORIZE=${KERL_COLORIZE:=$KERL_COLOR_AVAILABLE} if [ -n "$OTP_GITHUB_URL" ]; then _OGU="$OTP_GITHUB_URL" fi if [ -n "$KERL_CONFIGURE_OPTIONS" ]; then _KCO="$KERL_CONFIGURE_OPTIONS" fi if [ -n "$KERL_CONFIGURE_APPLICATIONS" ]; then _KCA="$KERL_CONFIGURE_APPLICATIONS" fi if [ -n "$KERL_CONFIGURE_DISABLE_APPLICATIONS" ]; then _KCDA="$KERL_CONFIGURE_DISABLE_APPLICATIONS" fi if [ -n "$KERL_SASL_STARTUP" ]; then _KSS="$KERL_SASL_STARTUP" fi if [ -n "$KERL_DEPLOY_SSH_OPTIONS" ]; then _KDSSH="$KERL_DEPLOY_SSH_OPTIONS" fi if [ -n "$KERL_DEPLOY_RSYNC_OPTIONS" ]; then _KDRSYNC="$KERL_DEPLOY_RSYNC_OPTIONS" fi if [ -n "$KERL_INSTALL_MANPAGES" ]; then _KIM="$KERL_INSTALL_MANPAGES" fi if [ -n "$KERL_INSTALL_HTMLDOCS" ]; then _KIHD="$KERL_INSTALL_HTMLDOCS" fi if [ -n "$KERL_BUILD_PLT" ]; then _KBPLT="$KERL_BUILD_PLT" fi if [ -n "$KERL_BUILD_DOCS" ]; then _KBD="$KERL_BUILD_DOCS" fi if [ -n "$KERL_DOC_TARGETS" ]; then _KDT="$KERL_DOC_TARGETS" fi if [ -n "$KERL_BUILD_BACKEND" ]; then _KBB="$KERL_BUILD_BACKEND" fi if [ -n "$KERL_RELEASE_TARGET" ]; then _KRT="$KERL_RELEASE_TARGET" fi OTP_GITHUB_URL=https://github.com/erlang/otp KERL_CONFIGURE_OPTIONS= KERL_CONFIGURE_APPLICATIONS= KERL_CONFIGURE_DISABLE_APPLICATIONS= KERL_SASL_STARTUP= KERL_DEPLOY_SSH_OPTIONS= KERL_DEPLOY_RSYNC_OPTIONS= KERL_INSTALL_MANPAGES= KERL_INSTALL_HTMLDOCS= KERL_BUILD_PLT= KERL_BUILD_DOCS= KERL_DOC_TARGETS=chunks KERL_BUILD_BACKEND= KERL_RELEASE_TARGET= # ensure the base dir exists mkdir -p "$KERL_BASE_DIR" || exit 1 # source the config file if available if [ -f "$KERL_CONFIG" ]; then # shellcheck source=/dev/null . "$KERL_CONFIG" fi if [ -n "$_OGU" ]; then OTP_GITHUB_URL="$_OGU" fi if [ -n "$_KCO" ]; then KERL_CONFIGURE_OPTIONS="$_KCO" fi if [ -n "$_KCA" ]; then KERL_CONFIGURE_APPLICATIONS="$_KCA" fi if [ -n "$_KCDA" ]; then KERL_CONFIGURE_DISABLE_APPLICATIONS="$_KCDA" fi if [ -n "$_KSS" ]; then KERL_SASL_STARTUP="$_KSS" fi if [ -n "$_KDSSH" ]; then KERL_DEPLOY_SSH_OPTIONS="$_KDSSH" fi if [ -n "$_KDRSYNC" ]; then KERL_DEPLOY_RSYNC_OPTIONS="$_KDRSYNC" fi if [ -n "$_KIM" ]; then KERL_INSTALL_MANPAGES="$_KIM" fi if [ -n "$_KIHD" ]; then KERL_INSTALL_HTMLDOCS="$_KIHD" fi if [ -n "$_KBPLT" ]; then KERL_BUILD_PLT="$_KBPLT" fi if [ -n "$_KBD" ]; then KERL_BUILD_DOCS="$_KBD" fi if [ -n "$_KDT" ]; then KERL_DOC_TARGETS="$_KDT" fi if [ -n "$_KBB" ]; then KERL_BUILD_BACKEND="$_KBB" fi if [ -n "$_KRT" ]; then KERL_RELEASE_TARGET="$_KRT" fi if [ -z "$KERL_SASL_STARTUP" ]; then INSTALL_OPT='-minimal' else INSTALL_OPT='-sasl' fi if [ -z "$KERL_BUILD_BACKEND" ]; then KERL_BUILD_BACKEND='git' fi if [ "$KERL_BUILD_BACKEND" = 'git' ]; then KERL_USE_AUTOCONF=1 elif [ "$KERL_BUILD_BACKEND" != 'tarball' ]; then l=e stderr "Unhandled value KERL_BUILD_BACKEND='${KERL_BUILD_BACKEND}'" l=t stderr "KERL_BUILD_BACKEND must be one of 'git' (default) or 'tarball'" exit 1 fi KERL_SYSTEM=$(uname -s) case "$KERL_SYSTEM" in Darwin|FreeBSD|OpenBSD) MD5SUM='openssl md5' MD5SUM_FIELD=2 SED_OPT=-E ;; *) MD5SUM=md5sum MD5SUM_FIELD=1 SED_OPT=-r ;; esac usage() { stderr 'kerl: build and install Erlang/OTP' stderr "usage: $0 [options ...]" stderr '' stderr ' Command to be executed' stderr '' stderr 'Valid commands are:' stderr ' build Build specified release or git repository' stderr ' install Install the specified release at the given location' stderr ' build-install Builds and installs the specified release or git repository at the given location' stderr ' deploy Deploy the specified installation to the given host and location' stderr ' update Update the list of available releases from your source provider' stderr ' list List releases, builds and installations' stderr ' delete Delete builds and installations' stderr ' path Print the path of a given installation' stderr ' active Print the path of the active installation' stderr ' plt Print Dialyzer PLT path for the active installation' stderr ' status Print available builds and installations' stderr ' prompt Print a string suitable for insertion in prompt' stderr ' cleanup Remove compilation artifacts (use after installation)' stderr ' emit-activate Print the activate script' stderr " version Print current version (current: $KERL_VERSION)" exit 1 } if [ $# -eq 0 ]; then usage; fi get_releases() { if [ "$KERL_BUILD_BACKEND" = 'git' ]; then get_git_releases else get_tarball_releases fi } get_git_releases() { tmp="$(mktemp "$TMP_DIR"/kerl.XXXXXX)" git ls-remote --tags --refs "$OTP_GITHUB_URL" > "$tmp" ret=$? if [ $ret -eq 0 ]; then < "$tmp" cut -f2 \ | cut -d'/' -f3- \ | sed $SED_OPT \ -e '# Delete all tags starting with ":" as to not mix' \ -e '# them with the prefixed lines we`re generating next.' \ -e '/^:/d' \ \ -e '# Prefix "OTP*" release lines with the crux of their versions.' \ -e '# - "OTP_R16B01_RC1" => ":16B01_RC1 OTP_R16B01_RC1"' \ -e '# - "OTP_R16B03" => ":16B03 OTP_R16B03"' \ -e '# - "OTP-17.0" => ":17.0 OTP-17.0"' \ -e '# - "OTP-17.3.1" => ":17.3.1 OTP-17.3.1"' \ -e '# - "OTP-19.0-rc1" => ":19.0-rc1 OTP-19.0-rc1"' \ -e '# - "OTP-19.0-rc2" => ":19.0-rc2 OTP-19.0-rc2"' \ -e 's/^(OTP[-_](R?([0-9][^ :]*).*))/:\3 \2/' \ \ -e '# Delete all lines that didn`t get prefixed above.' \ -e '/^[^:]/d' \ \ -e '# Move the colon markers preceding each version prefix' \ -e '# as to preceed the tag suffix instead, which will make' \ -e '# throwing the version prefixes easier later on.' \ -e '# - ":16B01_RC1 OTP_R16B03" => "16B01_RC1 :OTP_R16B01_RC1"' \ -e '# - ":16B03 OTP_R16B03" => "16B03 :OTP_R16B03"' \ -e '# - ":17.0 OTP_R16B03" => "17.0 :OTP-17.0"' \ -e '# - ":17.3.1 OTP_R16B03" => "17.3.1 :OTP-17.3.1"' \ -e '# - ":19.0-rc1 OTP_R16B03" => "19.0-rc1 :OTP-19.0-rc1"' \ -e '# - ":19.0-rc2 OTP_R16B03" => "19.0-rc2 :OTP-19.0-rc2"' \ -e 's/^:([^ ]+) /\1 :/' \ \ -e '# Repeatedly replace sequences of one or more dots, dashes' \ -e '# or underscores, within each version prefix, with single' \ -e '# space characters.' \ -e '# - "16B01_RC1 :OTP_R16B01_RC1" => "16B01 RC1 :OTP_R16B01_RC1"' \ -e '# - "16B03 :OTP_R16B03" => "16B03 :OTP_R16B03"' \ -e '# - "17.0 :OTP-17.0" => "17 0 :OTP-17.0"' \ -e '# - "17.3.1 :OTP-17.3.1" => "17 3 1 :OTP-17.3.1"' \ -e '# - "19.0-rc1 :OTP-19.0-rc1" => "19 0 rc1 :OTP-19.0-rc1"' \ -e '# - "19.0-rc2 :OTP-19.0-rc2" => "19 0 rc2 :OTP-19.0-rc2"' \ -e ':loop' \ -e 's/^([^:]*)[.-]+([^:]*) :/\1 \2 :/' \ -e 't loop' \ \ -e '# Repeatedly replace "A", "B", or "C" separators, within each' \ -e '# version prefix, with " 0 ", " 1 " and " 2 ", respectively.' \ -e '# - "16B01 RC1 :OTP_R16B01_RC1" => "16 1 01 RC1 :OTP_R16B01_RC1"' \ -e '# - "16B03 :OTP_R16B03" => "16 1 03 :OTP_R16B03"' \ -e ':loop2' \ -e 's/^(.*[0-9]+)A([^:]*) :/\1 0 \2 :/' \ -e 's/^(.*[0-9]+)B([^:]*) :/\1 1 \2 :/' \ -e 's/^(.*[0-9]+)C([^:]*) :/\1 2 \2 :/' \ -e 't loop2' \ \ -e '# Repeatedly replace space-release candidate infixes, within' \ -e '# each version prefix, with a leading zero followed by' \ -e '# the candidate number.' \ -e '# - "16 1 01 RC1 :OTP_R16B01_RC1" => "16 1 01 0 1 :OTP_R16B01_RC1"' \ -e '# - "19 0 rc1 :OTP-19.0-rc1" => "19 0 0 1 :OTP-19.0-rc1"' \ -e '# - "19 0 rc2 :OTP-19.0-rc2" => "19 0 0 2 :OTP-19.0-rc2"' \ -e ':loop3' \ -e 's/^([^:]* )(rc|RC)([0-9]+)(( [^:]*)?) :/\10 \3\4 :/' \ -e 't loop3' \ \ -e '# Repeatedly prefix single digits, within each version prefix,' \ -e '# with leading zeroes.' \ -e '# - "16 1 01 0 1 :OTP_R16B01_RC1" => "16 01 01 00 01 :OTP_R16B01_RC1"' \ -e '# - "16 1 03 :OTP_R16B03" => "16 01 03 :OTP_R16B03"' \ -e '# - "17 0 :OTP-17.0" => "17 00 :OTP-17.0"' \ -e '# - "17 3 1 :OTP-17.3.1" => "17 03 01 :OTP-17.3.1"' \ -e '# - "19 0 0 1 :OTP-19.0-rc1" => "19 00 00 01 :OTP-19.0-rc.1"' \ -e '# - "19 0 0 2 :OTP-19.0-rc2" => "19 00 00 02 :OTP-19.0-rc.2"' \ -e ':loop4' \ -e 's/^([^:]*[^0-9:])([0-9])(([^0-9][^:]*)?) :/\10\2\3 :/' \ -e 't loop4' \ \ -e '# Suffix each version prefix with 00 as to not compare ':' with a number.' \ -e '# - "16 01 01 00 01 :OTP_R16B01_RC1" => "16 01 01 00 01 00 :OTP_R16B01_RC1"' \ -e '# - "16 01 03 :OTP_R16B03" => "16 01 03 00 :OTP_R16B03"' \ -e '# - "17 00 :OTP-17.0"" => "17 00 00 :OTP-17.0"' \ -e '# - "17 03 01 :OTP-17.3.1" => "17 03 01 00 :OTP-17.3.1"' \ -e '# - "19 00 00 01 :OTP-19.0-rc.1" => "19 00 00 01 00 :OTP-19.0-rc.1"' \ -e '# - "19 00 00 02 :OTP-19.0-rc.2" => "19 00 00 02 00 :OTP-19.0-rc.2"' \ -e 's/^([^:]+) :/\1 00 :/' \ \ | LC_ALL=C sort -n \ | cut -d':' -f2- fi rm -f "$tmp" return $ret } get_tarball_releases() { tmp="$(mktemp "$TMP_DIR"/kerl.XXXXXX)" if [ 200 = "$(\curl -qsL --output "$tmp" --write-out '%{http_code}' $ERLANG_DOWNLOAD_URL/)" ]; then sed $SED_OPT \ -e 's/^.*<[aA] [hH][rR][eE][fF]=\"otp_src_([-0-9A-Za-z_.]+)\.tar\.gz\">.*$/\1/' \ -e '/^R1|^[0-9]/!d' "$tmp" \ | sed -e 's/^R\(.*\)/\1:R\1/' \ | sed -e 's/^\([^\:]*\)$/\1-z:\1/' \ | sort | cut -d: -f2 rm "$tmp" return 0 fi rm "$tmp" exit 1 } update_checksum_file() { if [ "$KERL_BUILD_BACKEND" = 'git' ]; then return 0 else l=n stderr 'Getting checksum file from erlang.org...' curl -f -L -o "$KERL_DOWNLOAD_DIR"/MD5 "$ERLANG_DOWNLOAD_URL"/MD5 || exit 1 fi } ensure_checksum_file() { if [ ! -s "$KERL_DOWNLOAD_DIR"/MD5 ]; then update_checksum_file fi } check_releases() { if [ ! -f "$KERL_BASE_DIR"/otp_releases ]; then get_releases >"$KERL_BASE_DIR"/otp_releases fi } is_valid_release() { check_releases while read -r rel; do if [ "$1" = "$rel" ]; then return 0 fi done <"$KERL_BASE_DIR"/otp_releases return 1 } assert_valid_release() { if ! is_valid_release "$1"; then l=e stderr "$1 is not a valid Erlang/OTP release" exit 1 fi return 0 } get_release_from_name() { if [ -f "$KERL_BASE_DIR"/otp_builds ]; then while read -r l; do rel=$(echo "$l" | cut -d, -f1) name=$(echo "$l" | cut -d, -f2) if [ "$name" = "$1" ]; then echo "$rel" return 0 fi done <"$KERL_BASE_DIR"/otp_builds fi return 1 } get_newest_valid_release() { check_releases rel=$(tail -1 "$KERL_BASE_DIR"/otp_releases) if [ -n "$rel" ]; then echo "$rel" return 0 fi return 1 } is_valid_installation() { if [ -f "$KERL_BASE_DIR"/otp_installations ]; then while read -r l; do name=$(echo "$l" | cut -d' ' -f1) path=$(echo "$l" | cut -d' ' -f2) if [ "$name" = "$1" ] || [ "$path" = "$1" ]; then if [ -f "$path"/activate ]; then return 0 fi fi done <"$KERL_BASE_DIR"/otp_installations fi return 1 } assert_valid_installation() { if ! is_valid_installation "$1"; then l=e stderr "$1 is not a kerl-managed Erlang/OTP installation" exit 1 fi return 0 } assert_build_name_unused() { if [ -f "$KERL_BASE_DIR"/otp_builds ]; then while read -r l; do name=$(echo "$l" | cut -d, -f2) if [ "$name" = "$1" ]; then l=e stderr "There's already a build named $1" exit 1 fi done <"$KERL_BASE_DIR"/otp_builds fi } _check_required_pkgs() { has_dpkg=$(command -v dpkg) has_rpm=$(command -v rpm) has_apk=$(command -v apk) if [ -n "$has_dpkg" ] || [ -n "$has_rpm" ] || [ -n "$has_apk" ]; then # found either dpkg, rpm or apk (or maybe even both!) if [ -n "$has_dpkg" ] && [ -n "$has_rpm" ]; then l=w stderr 'WARNING: You appear to have BOTH rpm and dpkg. This is very strange. No package checks done.' elif [ -n "$has_dpkg" ] && [ -n "$has_apk" ]; then l=w stderr 'WARNING: You appear to have BOTH apk and dpkg. This is very strange. No package checks done.' elif [ -n "$has_rpm" ] && [ -n "$has_apk" ]; then l=w stderr 'WARNING: You appear to have BOTH apk and rpm. This is very strange. No package checks done.' elif [ -n "$has_dpkg" ]; then _check_dpkg elif [ -n "$has_rpm" ]; then _check_rpm elif [ -n "$has_apk" ]; then _check_apk fi fi } _dpkg_is_installed() { # gratefully stolen from # https://superuser.com/questions/427318/test-if-a-package-is-installed-in-apt # returns 0 (true) if found, 1 otherwise dpkg-query -Wf'${db:Status-abbrev}' "$1" 2>/dev/null | \grep -q '^i' } _check_dpkg() { required=' libssl-dev make automake autoconf libncurses5-dev gcc g++ ' for pkg in $required; do if ! _dpkg_is_installed "$pkg"; then l=w stderr "WARNING: It appears that a required development package '$pkg' is not installed." fi done } _rpm_is_installed() { rpm --quiet -q "$1" >/dev/null 2>&1 } _check_rpm() { required=' openssl-devel make automake autoconf ncurses-devel gcc g++ ' for pkg in $required; do if ! _rpm_is_installed "$pkg"; then l=w stderr "WARNING: It appears a required development package '$pkg' is not installed." fi done } _apk_is_installed() { apk -e info "$1" >/dev/null 2>&1 } _check_apk() { required=' openssl-dev make autoconf ncurses-dev gcc g++ ' for pkg in $required; do if ! _apk_is_installed "$pkg"; then l=w stderr "WARNING: It appears a required development package '$pkg' is not installed." fi done } do_git_build() { git_url=$1 git_version=$2 build_name=$3 assert_build_name_unused "$build_name" GIT=$(printf '%s' "$git_url" | $MD5SUM | cut -d ' ' -f $MD5SUM_FIELD) mkdir -p "$KERL_GIT_DIR" || exit 1 cd "$KERL_GIT_DIR" || exit 1 l=n stderr "Checking out Erlang/OTP git repository from $git_url..." if [ ! -d "$GIT" ]; then if ! git clone -q --mirror "$git_url" "$GIT" >/dev/null 2>&1; then l=e stderr 'Error mirroring remote git repository' exit 1 fi fi cd "$GIT" || exit 1 if ! git remote update --prune >/dev/null 2>&1; then l=e stderr 'Error updating remote git repository' exit 1 fi rm -Rf "${KERL_BUILD_DIR:?}/$build_name" mkdir -p "$KERL_BUILD_DIR/$build_name" || exit 1 cd "$KERL_BUILD_DIR/$build_name" || exit 1 if ! git clone -l "$KERL_GIT_DIR/$GIT" otp_src_git >/dev/null 2>&1; then l=e stderr 'Error cloning local git repository' exit 1 fi cd otp_src_git || exit 1 if ! git checkout "$git_version" >/dev/null 2>&1; then if ! git checkout -b "$git_version" "$git_version" >/dev/null 2>&1; then l=e stderr 'Could not checkout specified version' rm -Rf "${KERL_BUILD_DIR:?}/$build_name" exit 1 fi fi if [ ! -x otp_build ]; then l=e stderr 'Not a valid Erlang/OTP repository' rm -Rf "${KERL_BUILD_DIR:?}/$build_name" exit 1 fi l=n stderr "Building Erlang/OTP $git_version from git, please wait..." if [ -z "$KERL_BUILD_AUTOCONF" ]; then KERL_USE_AUTOCONF=1 fi _do_build 'git' "$build_name" l=s stderr "Erlang/OTP $build_name from git has been successfully built" list_add builds git,"$build_name" } get_otp_version() { echo "$1" | sed $SED_OPT -e 's/R?([0-9]{1,2}).+/\1/' } get_perl_version() { if assert_perl; then # This is really evil but it's portable and it works. Don't @ me bro perl -e 'print int(($] - 5)*1000)' else l=e stderr 'FATAL: could not find perl which is required to compile Erlang.' exit 1 fi } assert_perl() { perl_loc=$(command -v perl) if [ -z "$perl_loc" ]; then return 1 else # 0 to bash is "true" because of Unix exit code conventions return 0 fi } get_javac_version() { java_loc=$(command -v javac) if [ -z "$java_loc" ]; then # Java's not installed, so just return 0 0 else javaout=$(javac -version 2>&1) echo "$javaout" | cut -d' ' -f2 | cut -d. -f2 fi } show_configuration_warnings() { # $1 is logfile # $2 is section header (E.g. "APPLICATIONS DISABLED") # Find the row number for the section we are looking for INDEX=$(\grep -n -m1 "$2" "$1" | cut -d: -f1) # If there are no warnings, the section won't appear in the log if [ -n "$INDEX" ]; then # Skip the section header, find the end line and skip it # then print the results indented tail -n +$((INDEX+3)) "$1" | \ sed -n '1,/\*/p' | \ awk -F: -v logfile="$1" -v section="$2" \ 'BEGIN { printf "%s (See: %s)\n", section, logfile } /^[^\*]/ { print " *", $0 } END { print "" } ' fi } show_logfile() { l=e stderr "$1" tail "$2" 1>&2 stderr "" l=t stderr "Please see $2 for full details." } maybe_patch() { # $1 = OS platform e.g., Darwin, etc # $2 = OTP release release=$(get_otp_version "$2") case "$1" in Darwin) maybe_patch_darwin "$release" # Catalina and clang require a "no-weaks-import" flag during build maybe_patch_catalina "$release" "$2" maybe_patch_macos_version_check "$release" ;; SunOS) maybe_patch_sunos "$release" ;; *) ;; esac maybe_patch_all "$release" } maybe_patch_all() { perlver=$(get_perl_version) if [ "$perlver" -ge 22 ]; then case "$1" in 14) apply_r14_beam_makeops_patch >>"$LOGFILE" ;; 15) apply_r15_beam_makeops_patch >>"$LOGFILE" ;; *) ;; esac fi # Are we building docs? if [ -n "$KERL_BUILD_DOCS" ]; then if [ "$1" -le 16 ]; then javaver=$(get_javac_version) if [ "$javaver" -ge 8 ]; then apply_javadoc_linting_patch >>"$LOGFILE" fi fi fi # Maybe apply zlib patch if [ "$1" -ge 17 ] && [ "$1" -le 19 ]; then apply_zlib_patch >> "$LOGFILE" fi } maybe_patch_darwin() { # Reminder: $1 = OTP release version if [ "$1" -le 14 ]; then CFLAGS='-DERTS_DO_INCL_GLB_INLINE_FUNC_DEF' apply_darwin_compiler_patch >>"$LOGFILE" elif [ "$1" -eq 16 ]; then apply_r16_wx_ptr_patch >>"$LOGFILE" elif [ "$1" -ge 17 ] && [ "$1" -le 19 ]; then apply_wx_ptr_patch >>"$LOGFILE" elif [ "$1" -ge 21 ] && [ "$1" -le 23 ]; then apply_in6addr_test_patch >> "$LOGFILE" KERL_USE_AUTOCONF=1 fi } maybe_patch_catalina() { clang_version=$(/usr/bin/clang --version | head -1 | awk '{print $4}') command_line_tools_version=$(xcode-select -v | awk '{print $3}' | sed 's/.$//') release="$1" otp_version="$2" if is_osx_catalina && \ [ "$release" -lt 23 ] && \ compare_sem_version "$otp_version" "<" "22.3.1" && \ compare_sem_version "$clang_version" ">" "11.4" && \ [ "$command_line_tools_version" -le 2373 ]; then apply_catalina_no_weak_imports_patch >>"$LOGFILE" KERL_USE_AUTOCONF=1 fi } is_osx() { uname -r | \grep "^$1.*$" > /dev/null } is_osx_catalina() { is_osx 19 } compare_sem_version() { version=$1 operator=$2 baseline=$3 case $operator in '<') [ "$(printf "%s\n%s" "$version" "$baseline" | sort --version-sort | head -n 1)" != "$baseline" ] ;; ('>' | "==") [ "$(printf "%s\n%s" "$version" "$baseline" | sort --version-sort | head -n 1)" = "$baseline" ] ;; esac } apply_catalina_no_weak_imports_patch() { patch -N -p1 <<'_END_PATCH' diff --git a/erts/configure.in b/erts/configure.in index 3ba8216a19..d7cebc5ebc 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -926,20 +926,16 @@ dnl for now that is the way we do it. USER_LD=$LD USER_LDFLAGS="$LDFLAGS" LD='$(CC)' + case $host_os in - darwin*) - saved_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -Wl,-no_weak_imports" - AC_TRY_LINK([],[], - [ - LD_MAY_BE_WEAK=no - ], - [ - LD_MAY_BE_WEAK=yes - LDFLAGS="$saved_LDFLAGS" - ]);; - *) - LD_MAY_BE_WEAK=no;; + darwin19*) + # Disable stack checking to avoid crashing with a segment fault + # in macOS Catalina. + AC_MSG_NOTICE([Turning off stack check on macOS 10.15 (Catalina)]) + CFLAGS="-Wno-error=implicit-function-declaration -fno-stack-check $CFLAGS" + ;; + *) + ;; esac AC_SUBST(LD) _END_PATCH } # https://github.com/erlang/otp/commit/f1044ef9e35da26f276b8127640e177d67aade6a.diff maybe_patch_macos_version_check() { release="$1" if is_osx_bigsur || is_osx_monterey || is_osx_ventura && \ [ "$release" -lt 24 ]; then apply_bypass_version_check >>"$LOGFILE" KERL_USE_AUTOCONF=1 fi } is_osx_bigsur() { is_osx 20 } is_osx_monterey() { is_osx 21 } is_osx_ventura() { is_osx 22 } apply_bypass_version_check() { if [ -f make/configure.in ]; then l=n stderr "Bypassing version check in make/configure.in" # shellcheck disable=SC2016 sed -i '' 's/\(#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > $int_macosx_version\)/\1 \&\& false/' make/configure.in elif [ -f configure.in ]; then l=n stderr "Bypassing version check in configure.in" # shellcheck disable=SC2016 sed -i '' 's/\(#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > $int_macosx_version\)/\1 \&\& false/' configure.in l=n stderr "Removing no_weak_imports from LDFLAGS in erts/configure.in" # shellcheck disable=SC2016 sed -i '' 's/LDFLAGS="$LDFLAGS -Wl,-no_weak_imports"//' erts/configure.in fi } maybe_patch_sunos() { if [ "$1" -le 14 ]; then apply_solaris_networking_patch >>"$LOGFILE" fi } do_normal_build() { assert_valid_release "$1" assert_build_name_unused "$2" FILENAME="" download "$1" mkdir -p "$KERL_BUILD_DIR/$2" || exit 1 if [ ! -d "$KERL_BUILD_DIR/$2/$FILENAME" ]; then l=n stderr 'Extracting source code' UNTARDIRNAME="$KERL_BUILD_DIR/$2/$FILENAME-kerluntar-$$" rm -rf "$UNTARDIRNAME" mkdir -p "$UNTARDIRNAME" || exit 1 # github tarballs have a directory in the form of "otp[_-]TAGNAME" # Ericsson tarballs have the classic otp_src_RELEASE pattern # Standardize on Ericsson format because that's what the rest of the script expects (cd "$UNTARDIRNAME" && tar xzf "$KERL_DOWNLOAD_DIR/$FILENAME".tar.gz && \ cp -rfp ./* "$KERL_BUILD_DIR/$2/otp_src_$1") rm -rf "$UNTARDIRNAME" fi l=n stderr "Building Erlang/OTP $1 ($2), please wait..." _do_build "$1" "$2" l=s stderr "Erlang/OTP $1 ($2) has been successfully built" list_add builds "$1,$2" } _flags() { # We used to munge the LD and DED flags for clang 9/10 shipped with # High Sierra (macOS 10.13), Mojave (macOS 10.14) and Catalina # (macOS 10.15) # # As of OTP 20.1 that is (apparently) no longer necessary and # in OTP 24 breaks stuff. See thread and comment here: # https://github.com/erlang/otp/issues/4821#issuecomment-845914942 case "$KERL_SYSTEM" in Darwin) # Make sure we don't overwrite stuff that someone who # knows better than us set. if [ -z "$CC" ]; then CC='clang' fi CFLAGS="$CFLAGS" CC="$CC" "$@" ;; *) CFLAGS="$CFLAGS" "$@" ;; esac } _do_build() { case "$KERL_SYSTEM" in Darwin) # Ensure that the --enable-darwin-64bit flag is set on all macOS # That way even on older Erlangs we get 64 bit Erlang builds # macOS has been mandatory 64 bit for a while if ! echo "$KERL_CONFIGURE_OPTIONS" | \grep 'darwin-64bit' >/dev/null 2>&1; then KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS "--enable-darwin-64bit fi # Attempt to use brew to discover if and where openssl has been # installed unless the user has already explictly set it. if ! echo "$KERL_CONFIGURE_OPTIONS" | \grep 'with-ssl' >/dev/null 2>&1; then whichbrew=$(command -v brew) if [ -n "$whichbrew" ] && [ -x "$whichbrew" ]; then brew_prefix=$(brew --prefix openssl@1.1) if [ -n "$brew_prefix" ] && [ -d "$brew_prefix" ]; then KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS "--with-ssl=$brew_prefix fi fi fi ;; Linux) # we are going to check here to see if the Linux uses dpkg or rpms # # this is a "best effort" attempt to discover if a Linux has the # packages needed to build Erlang. We will always assume the user # knows better than us and are going to go ahead and try to build # Erlang anyway. But at least it will be a clear warning to the # user if a build fails. _check_required_pkgs ;; *) ;; esac ERL_TOP="$KERL_BUILD_DIR/$2/otp_src_$1" cd "$ERL_TOP" || exit 1 LOGFILE="$KERL_BUILD_DIR/$2/otp_build_$1.log" # Add a build date and env in logfile echo "*** $(date) - kerl build $1 ***" >> "$LOGFILE" env | grep KERL_BUILD_ | xargs -n1 echo "*" >> "$LOGFILE" # Set configuation flags given applications white/black lists if [ -n "$KERL_CONFIGURE_APPLICATIONS" ]; then for app in $KERL_CONFIGURE_APPLICATIONS; do case "$KERL_CONFIGURE_OPTIONS" in *"--with-$app"*) l=t stderr "Option '--with-$app' in KERL_CONFIGURE_OPTIONS is superfluous" ;; *) KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS --with-$app" ;; esac done fi if [ -n "$KERL_CONFIGURE_DISABLE_APPLICATIONS" ]; then for app in $KERL_CONFIGURE_DISABLE_APPLICATIONS; do case "$KERL_CONFIGURE_OPTIONS" in *"--without-$app"*) l=t stderr "Option '--without-$app' in KERL_CONFIGURE_OPTIONS is superfluous" ;; *) KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS --without-$app" ;; esac done fi # Check to see if configuration options need to be stored or have changed TMPOPT="${TMP_DIR}/kerloptions.$$" echo "$CFLAGS" >"$TMPOPT" echo "$KERL_CONFIGURE_OPTIONS" >>"$TMPOPT" SUM=$($MD5SUM "$TMPOPT" | cut -d ' ' -f $MD5SUM_FIELD) # Check for a .kerl_config.md5 file if [ -e ./"$KERL_CONFIG_STORAGE_FILENAME".md5 ]; then # Compare our current options to the saved ones read -r OLD_SUM <./"$KERL_CONFIG_STORAGE_FILENAME".md5 if [ "$SUM" != "$OLD_SUM" ]; then l=n stderr 'Configure options have changed. Reconfiguring...' rm -f configure mv "$TMPOPT" ./"$KERL_CONFIG_STORAGE_FILENAME" echo "$SUM" >./"$KERL_CONFIG_STORAGE_FILENAME".md5 else # configure options are the same rm -f "$TMPOPT" fi else # no file exists, so write one mv "$TMPOPT" .kerl_config echo "$SUM" >.kerl_config.md5 fi # Don't apply patches to "custom" git builds. We have no idea if they will apply # cleanly or not. if [ "$1" != 'git' ]; then maybe_patch "$KERL_SYSTEM" "$1" fi if [ -n "$KERL_USE_AUTOCONF" ]; then # shellcheck disable=SC2086 if ! (./otp_build autoconf && \ _flags ./otp_build configure $KERL_CONFIGURE_OPTIONS >>"$LOGFILE") \ >> "$LOGFILE" 2>&1; then show_logfile 'Configure failed.' "$LOGFILE" list_remove builds "$1 $2" exit 1 fi else # shellcheck disable=SC2086 if ! _flags ./otp_build configure $KERL_CONFIGURE_OPTIONS >>"$LOGFILE" 2>&1; then show_logfile 'Configure failed.' "$LOGFILE" list_remove builds "$1 $2" exit 1 fi fi if echo "$KERL_CONFIGURE_OPTIONS" | \grep -- '--enable-native-libs' >/dev/null 2>&1; then make clean >>"$LOGFILE" 2>&1 # shellcheck disable=SC2086 if ! _flags ./otp_build configure $KERL_CONFIGURE_OPTIONS >>"$LOGFILE" 2>&1; then show_logfile 'Configure failed.' "$LOGFILE" autoclean "$2" list_remove builds "$1 $2" exit 1 fi fi for SECTION in 'APPLICATIONS DISABLED' \ 'APPLICATIONS INFORMATION' \ 'DOCUMENTATION INFORMATION'; do show_configuration_warnings "$LOGFILE" "$SECTION" done if [ -n "$KERL_CONFIGURE_APPLICATIONS" ]; then \find ./lib -maxdepth 1 -type d -exec touch -f {}/SKIP \; for app in $KERL_CONFIGURE_APPLICATIONS; do if ! rm ./lib/"$app"/SKIP; then l=e stderr "Couldn't prepare '$app' application for building" list_remove builds "$1 $2" autoclean "$2" exit 1 fi done fi if [ -n "$KERL_CONFIGURE_DISABLE_APPLICATIONS" ]; then for app in $KERL_CONFIGURE_DISABLE_APPLICATIONS; do if ! touch -f ./lib/"$app"/SKIP; then l=e stderr "Couldn't disable '$app' application for building" exit 1 fi done fi # shellcheck disable=SC2086 if ! _flags ./otp_build boot -a $KERL_CONFIGURE_OPTIONS >>"$LOGFILE" 2>&1; then show_logfile 'Build failed.' "$LOGFILE" autoclean "$2" list_remove builds "$1 $2" exit 1 fi if [ -n "$KERL_BUILD_DOCS" ]; then l=n stderr 'Building docs...' release=$(get_otp_version "$2") if ! make docs "DOC_TARGETS=$KERL_DOC_TARGETS" >>"$LOGFILE" 2>&1; then show_logfile 'Building docs failed.' "$LOGFILE" autoclean "$2" list_remove builds "$1 $2" exit 1 fi if ! make release_docs "DOC_TARGETS=$KERL_DOC_TARGETS" "RELEASE_ROOT=$KERL_BUILD_DIR/$2/release_$1" >>"$LOGFILE" 2>&1; then show_logfile 'Release of docs failed.' "$LOGFILE" autoclean "$2" list_remove builds "$1 $2" exit 1 fi fi if [ -n "$KERL_RELEASE_TARGET" ]; then # where `$TYPE` is `opt`, `gcov`, `gprof`, `debug`, `valgrind`, `asan` or `lcnt`. for r_type in $KERL_RELEASE_TARGET; do case $r_type in opt|debug) l=n stderr "Also building Erlang/OTP $1 ($2) $r_type VM, please wait..." cd "$ERL_TOP" || exit 1 if ! make TYPE="$r_type" ERL_TOP="$ERL_TOP" >>"$LOGFILE" 2>&1; then show_logfile "Build of $r_type VM failed." "$LOGFILE" list_remove builds "$1 $2" exit 1 fi ;; gcov|gprof|valgrind|asan|lcnt) l=n stderr "Also building Erlang/OTP $1 ($2) $r_type VM, please wait..." cd "$ERL_TOP/erts/emulator" || exit 1 if ! make "$r_type" ERL_TOP="$ERL_TOP" >>"$LOGFILE" 2>&1; then show_logfile "Build of $r_type VM failed." "$LOGFILE" list_remove builds "$1 $2" exit 1 fi ;; *) l=w stderr "Invalid '$r_type' runtime type" ;; esac done fi } do_build_install() { release_or_git=$1 git_url=$2 git_version=$3 build_name=$4 directory=$5 if is_valid_installation "$build_name"; then l=e stderr "There's already an installation named $build_name. Skipping build step..." exit 1 fi # This is also done on do_install, but saves the build time in case of error if ! is_valid_install_path "$directory"; then exit 1 fi if [ "$release_or_git" = "git" ]; then $0 build git "$git_url" "$git_version" "$build_name" else release="$release_or_git" $0 build "$release" "$build_name" fi status=$? if [ $status -ne 0 ]; then l=e stderr "Build failed! Skipping install step..." exit 1 fi $0 install "$build_name" "$directory" } # emit_activate: outputs (via cat) the content of the 'activate' script, as used by kerl (sh/bash) # @param $1/release: the argument in e.g. kerl build # @param $2/build_name: the argument in e.g. kerl build # @param $3/directory: the [directory] argument in e.g. kerl install [directory] emit_activate() { release=$1 build_name=$2 directory=$3 cat <<\ ======= #!/bin/sh # credits to virtualenv kerl_deactivate() { set -o allexport if [ -n "\${_KERL_ERL_AFLAGS_SET+x}" ]; then if [ -n "\$_KERL_ERL_AFLAGS_SET" ]; then ERL_AFLAGS="\$_KERL_SAVED_ERL_AFLAGS" else unset ERL_AFLAGS fi unset _KERL_SAVED_ERL_AFLAGS unset _KERL_ERL_AFLAGS_SET fi if [ -n "\$_KERL_PATH_REMOVABLE" ]; then # shellcheck disable=SC2001 PATH="\$(echo "\$PATH" | sed -e "s#\$_KERL_PATH_REMOVABLE:##")" unset _KERL_PATH_REMOVABLE fi if [ -n "\$_KERL_MANPATH_REMOVABLE" ]; then # shellcheck disable=SC2001 MANPATH="\$(echo "\$MANPATH" | sed -e "s#\$_KERL_MANPATH_REMOVABLE:##")" if [ -z "\$MANPATH" -a -z "\$_KERL_MANPATH_SET" ]; then unset MANPATH fi unset _KERL_MANPATH_REMOVABLE fi unset _KERL_MANPATH_SET if [ -n "\${_KERL_REBAR_PLT_DIR_SET+x}" ]; then if [ -n "\$_KERL_REBAR_PLT_DIR_SET" ]; then REBAR_PLT_DIR="\$_KERL_SAVED_REBAR_PLT_DIR" else unset REBAR_PLT_DIR fi unset _KERL_SAVED_REBAR_PLT_DIR unset _KERL_REBAR_PLT_DIR_SET fi if [ -n "\${_KERL_REBAR_CACHE_DIR_SET+x}" ]; then if [ -n "\$_KERL_REBAR_CACHE_DIR_SET" ]; then REBAR_CACHE_DIR="\$_KERL_SAVED_REBAR_CACHE_DIR" else unset REBAR_CACHE_DIR fi unset _KERL_SAVED_REBAR_CACHE_DIR unset _KERL_REBAR_CACHE_DIR_SET fi if [ -n "\$_KERL_ACTIVE_DIR" ]; then unset _KERL_ACTIVE_DIR fi if [ -n "\$_KERL_SAVED_PS1" ]; then PS1="\$_KERL_SAVED_PS1" unset _KERL_SAVED_PS1 fi if [ -n "\$_KERL_ERL_CALL_REMOVABLE" ]; then # shellcheck disable=SC2001 PATH="\$(echo "\$PATH" | sed -e "s#\$_KERL_ERL_CALL_REMOVABLE:##")" fi unset _KERL_ERL_CALL_REMOVABLE if [ -n "\$BASH" ] || [ -n "\$ZSH_VERSION" ]; then hash -r fi if [ ! "\$1" = "nondestructive" ]; then unset -f kerl_deactivate fi unset KERL_ENABLE_PROMPT unset KERL_PROMPT_FORMAT set +o allexport } kerl_deactivate nondestructive _KERL=$(command -v kerl) if [ -n "\$_KERL" ]; then _KERL_VERSION=\$(\$_KERL version) fi if [ -n "\$_KERL_VERSION" ] && [ "\$_KERL_VERSION" != "$KERL_VERSION" ]; then echo "WARNING: this Erlang/OTP installation appears to be stale. Please consider reinstalling." echo " It was created with kerl $KERL_VERSION, and the current version is \$_KERL_VERSION." fi unset _KERL_VERSION _KERL set -o allexport _KERL_SAVED_REBAR_CACHE_DIR="\$REBAR_CACHE_DIR" _KERL_REBAR_CACHE_DIR_SET="\${REBAR_CACHE_DIR+x}" _KERL_SAVED_REBAR_CACHE_DIR="\$REBAR_CACHE_DIR" _KERL_REBAR_PLT_DIR_SET="\${REBAR_PLT_DIR+x}" _KERL_SAVED_REBAR_PLT_DIR="\$REBAR_PLT_DIR" _KERL_PATH_REMOVABLE="$directory/bin" PATH="\${_KERL_PATH_REMOVABLE}:\$PATH" _KERL_MANPATH_SET="\${MANPATH+x}" _KERL_MANPATH_REMOVABLE="$directory/lib/erlang/man:$directory/man" MANPATH="\${_KERL_MANPATH_REMOVABLE}:\$MANPATH" REBAR_CACHE_DIR="$directory/.cache/rebar3" REBAR_PLT_DIR="$directory" _KERL_ACTIVE_DIR="$directory" _KERL_ERL_CALL_REMOVABLE=\$(\\find $directory -type d -path "*erl_interface*/bin" 2>/dev/null) if [ -n "\$_KERL_ERL_CALL_REMOVABLE" ]; then PATH="\${_KERL_ERL_CALL_REMOVABLE}:\$PATH" fi # https://twitter.com/mononcqc/status/877544929496629248 _KERL_ERL_AFLAGS_SET="\${ERL_AFLAGS+x}" kernel_history=\$(echo "\$ERL_AFLAGS" | \\grep 'kernel shell_history' || true) if [ -z "\$kernel_history" ]; then if [ -n "\$ERL_AFLAGS" ]; then _KERL_SAVED_ERL_AFLAGS="\$ERL_AFLAGS" ERL_AFLAGS="-kernel shell_history enabled \$ERL_AFLAGS" else ERL_AFLAGS="-kernel shell_history enabled" fi fi unset kernel_history # shellcheck source=/dev/null if [ -f "$KERL_CONFIG" ]; then . "$KERL_CONFIG"; fi if [ -n "\$KERL_ENABLE_PROMPT" ]; then _KERL_SAVED_PS1="\$PS1" if [ -n "\$KERL_PROMPT_FORMAT" ]; then _KERL_PROMPT_FORMAT="\$KERL_PROMPT_FORMAT" else _KERL_PROMPT_FORMAT="(%BUILDNAME%)" fi _KERL_PRMPT=\$(echo "\$_KERL_PROMPT_FORMAT" | sed 's^%RELEASE%^$release^;s^%BUILDNAME%^$build_name^') PS1="\$_KERL_PRMPT\$PS1" unset _KERL_PRMPT _KERL_PROMPT_FORMAT fi if [ -n "\$BASH" ] || [ -n "\$ZSH_VERSION" ]; then hash -r fi set +o allexport ======= } # emit_activate_fish: outputs (via cat) the content of the 'activate' script, as used by kerl (fish) # @param $1/release: the argument in e.g. kerl build # @param $2/build_name: the argument in e.g. kerl build # @param $3/directory: the [directory] argument in e.g. kerl install [directory] emit_activate_fish() { release=$1 build_name=$2 directory=$3 cat <<\ ======= if functions -q kerl_deactivate kerl_deactivate end set _KERL (command -v kerl) if test -n "\$_KERL" set _KERL_VERSION (\$_KERL version) end if test -n "\$_KERL_VERSION" -a "\$_KERL_VERSION" != "$KERL_VERSION" echo "WARNING: this Erlang/OTP installation appears to be stale. Please consider reinstalling." echo " It was created with kerl $KERL_VERSION, and the current version is \$_KERL_VERSION." end set -e _KERL_VERSION _KERL set -x _KERL_ACTIVE_DIR "$directory" set -p _KERL_CLEANUP "set -e _KERL_ACTIVE_DIR;" function _kerl_remove_el_ --description 'remove elements from array' set path_var \$argv[1] set elements \$argv[2] echo " for el in \\\$\$elements; if set -l index (contains -i -- \\\$el \\\$\$path_var); set -e \$path_var[1][\\\$index]; end; end; " end set -x _KERL_PATH_REMOVABLE "$directory/bin" set -l _KERL_ERL_CALL_REMOVABLE (find "$directory" -type d -path "*erl_interface*/bin" 2>/dev/null) if test -n "\$_KERL_ERL_CALL_REMOVABLE" set -a _KERL_PATH_REMOVABLE \$_KERL_ERL_CALL_REMOVABLE end set -p _KERL_CLEANUP "set -e _KERL_PATH_REMOVABLE;" set -xp PATH \$_KERL_PATH_REMOVABLE set -p _KERL_CLEANUP (_kerl_remove_el_ PATH _KERL_PATH_REMOVABLE) set -x _KERL_MANPATH_REMOVABLE "$directory/lib/erlang/man" "$directory/man" set -p _KERL_CLEANUP "set -e _KERL_MANPATH_REMOVABLE;" if set -q MANPATH if test -n "\$MANPATH" set -xp MANPATH \$_KERL_MANPATH_REMOVABLE else set -x MANPATH \$_KERL_MANPATH_REMOVABLE end else set -x MANPATH \$_KERL_MANPATH_REMOVABLE set -p _KERL_CLEANUP " if test -z \"\\\$MANPATH\" set -e MANPATH end " end set -p _KERL_CLEANUP (_kerl_remove_el_ MANPATH _KERL_MANPATH_REMOVABLE) functions -e _kerl_remove_el_ if set -q REBAR_PLT_DIR set -p _KERL_CLEANUP "set -x REBAR_PLT_DIR \"\$REBAR_PLT_DIR\";" else set -p _KERL_CLEANUP "set -e REBAR_PLT_DIR;" end set -x REBAR_PLT_DIR "$directory" if set -q REBAR_CACHE_DIR set -p _KERL_CLEANUP "set -x REBAR_CACHE_DIR \"\$REBAR_CACHE_DIR\";" else set -p _KERL_CLEANUP "set -e REBAR_CACHE_DIR;" end set -x REBAR_CACHE_DIR "$directory/.cache/rebar3" if test -f "$KERL_CONFIG.fish" source "$KERL_CONFIG.fish" end if set --query KERL_ENABLE_PROMPT if functions -q fish_prompt functions --copy fish_prompt _kerl_saved_prompt set -p _KERL_CLEANUP " functions --copy _kerl_saved_prompt fish_prompt functions --erase _kerl_saved_prompt; " end function fish_prompt printf "%b" "($build_name)" _kerl_saved_prompt end set -p _KERL_CLEANUP "functions --erase fish_prompt;" end eval "function kerl_deactivate -S --description \"deactivate erlang environment\" \$_KERL_CLEANUP functions -e kerl_deactivate end" set -e _KERL_CLEANUP ======= } # emit_activate_csh: outputs (via cat) the content of the 'activate' script, as used by kerl (csh) # @param $1/release: the argument in e.g. kerl build # @param $2/build_name: the argument in e.g. kerl build # @param $3/directory: the [directory] argument in e.g. kerl install [directory] emit_activate_csh() { release=$1 build_name=$2 directory=$3 cat <<\ ======= # This file must be used with "source bin/activate.csh" *from csh*. # You cannot run it directly. alias kerl_deactivate '\\\\ test \$?_KERL_PATH_REMOVABLE != 0 && unset _KERL_PATH_REMOVABLE; \\\\ test \$?_KERL_MANPATH_REMOVABLE != 0 && unset _KERL_MANPATH_REMOVABLE; \\\\ test \$?_KERL_SAVED_PATH != 0 && setenv PATH "\$_KERL_SAVED_PATH" && unset _KERL_SAVED_PATH; rehash; \\\\ test \$?_KERL_SAVED_MANPATH != 0 && setenv MANPATH "\$_KERL_SAVED_MANPATH" && unset _KERL_SAVED_MANPATH; \\\\ test \$?_KERL_REBAR_PLT_DIR_SET != 0 && test "\$_KERL_REBAR_PLT_DIR_SET" == "no" && \\\\ unset _KERL_REBAR_PLT_DIR_SET && test \$?REBAR_PLT_DIR != 0 && unsetenv REBAR_PLT_DIR; \\\\ test \$?_KERL_REBAR_PLT_DIR_SET != 0 && test "\$_KERL_REBAR_PLT_DIR_SET" == "yes" && \\\\ unset _KERL_REBAR_PLT_DIR_SET && setenv REBAR_PLT_DIR "\$_KERL_SAVED_REBAR_PLT_DIR"; \\\\ test \$?_KERL_SAVED_REBAR_PLT_DIR && unset _KERL_SAVED_REBAR_PLT_DIR; \\\\ test \$?_KERL_REBAR_CACHE_DIR_SET != 0 && test "\$_KERL_REBAR_CACHE_DIR_SET" == "no" && \\\\ unset _KERL_REBAR_CACHE_DIR_SET && test \$?REBAR_CACHE_DIR != 0 && unsetenv REBAR_CACHE_DIR; \\\\ test \$?_KERL_REBAR_CACHE_DIR_SET != 0 && test "\$_KERL_REBAR_CACHE_DIR_SET" == "yes" && \\\\ unset _KERL_REBAR_CACHE_DIR_SET && setenv REBAR_CACHE_DIR "\$_KERL_SAVED_REBAR_CACHE_DIR"; \\\\ test \$?_KERL_SAVED_REBAR_CACHE_DIR && unset _KERL_SAVED_REBAR_CACHE_DIR; \\\\ test \$?_KERL_ACTIVE_DIR != 0 && unset _KERL_ACTIVE_DIR; \\\\ test \$?_KERL_ERL_CALL_REMOVABLE != 0 && unset _KERL_ERL_CALL_REMOVABLE; \\\\ test \$?_KERL_SAVED_PROMPT != 0 && set prompt="\$_KERL_SAVED_PROMPT" && unset _KERL_SAVED_PROMPT; \\\\ test "!:*" != "nondestructive" && unalias deactivate' # Unset irrelevant variables. kerl_deactivate nondestructive set _KERL = $(command -v kerl) if ( "\$_KERL" != "" ) then set _KERL_VERSION = \`\$_KERL version\` endif if ("\$_KERL_VERSION" != "" && "\$_KERL_VERSION" != "$KERL_VERSION") then echo "WARNING: this Erlang/OTP installation appears to be stale. Please consider reinstalling." echo " It was created with kerl $KERL_VERSION, and the current version is \$_KERL_VERSION." endif unset _KERL_VERSION _KERL if ( \$?REBAR_CACHE_DIR ) then set _KERL_REBAR_CACHE_DIR_SET = "yes" set _KERL_SAVED_REBAR_CACHE_DIR = "\$REBAR_CACHE_DIR" else set _KERL_REBAR_CACHE_DIR_SET = "no" set _KERL_SAVED_REBAR_CACHE_DIR = "" endif if ( \$?REBAR_PLT_DIR ) then set _KERL_REBAR_PLT_DIR_SET = "yes" set _KERL_SAVED_REBAR_PLT_DIR = "\$REBAR_PLT_DIR" else set _KERL_REBAR_PLT_DIR_SET = "no" set _KERL_SAVED_REBAR_PLT_DIR = "" endif set _KERL_PATH_REMOVABLE = "$directory/bin" set _KERL_SAVED_PATH = "\$PATH" setenv PATH "\${_KERL_PATH_REMOVABLE}:\$PATH" if ( ! \$?MANPATH ) then set MANPATH = "" endif set _KERL_MANPATH_REMOVABLE = "$directory/lib/erlang/man:$directory/man" set _KERL_SAVED_MANPATH = "\$MANPATH" setenv MANPATH "\${_KERL_MANPATH_REMOVABLE}:\$MANPATH" setenv REBAR_CACHE_DIR "$directory/.cache/rebar3" setenv REBAR_PLT_DIR "$directory" set _KERL_ACTIVE_DIR = "$directory" set _KERL_ERL_CALL_REMOVABLE = $(\find "$directory" -type d -path "*erl_interface*/bin") setenv PATH "\${_KERL_ERL_CALL_REMOVABLE}:\$PATH" if ( -f "$KERL_CONFIG.csh" ) then source "$KERL_CONFIG.csh" endif if ( \$?KERL_ENABLE_PROMPT ) then set _KERL_SAVED_PROMPT = "\$prompt" if ( \$?KERL_PROMPT_FORMAT ) then set FRMT = "\$KERL_PROMPT_FORMAT" else set FRMT = "(%BUILDNAME%)" endif set PROMPT = \$(echo "\$FRMT" | sed 's^%RELEASE%^$release^;s^%BUILDNAME%^$build_name^') set prompt = "\$PROMPT\$prompt" endif rehash ======= } do_install() { build_name=$1 if ! rel=$(get_release_from_name "$build_name"); then l=e stderr "No build named $build_name" exit 1 fi if ! is_valid_install_path "$2"; then exit 1 fi mkdir -p "$2" || exit 1 absdir=$(cd "$2" && pwd) l=n stderr "Installing Erlang/OTP $rel ($build_name) in $absdir..." ERL_TOP="$KERL_BUILD_DIR/$build_name/otp_src_$rel" LOGFILE="$KERL_BUILD_DIR/$build_name/otp_install_$rel.log" cd "$ERL_TOP" || exit 1 if ! (ERL_TOP="$ERL_TOP" ./otp_build release -a "$absdir" && cd "$absdir" && ./Install $INSTALL_OPT "$absdir") >> "$LOGFILE" 2>&1; then show_logfile "Install of Erlang/OTP $rel ($build_name) in $absdir failed" "$LOGFILE" exit 1 fi for r_type in $KERL_RELEASE_TARGET; do case "$r_type" in opt|debug) l=n stderr "Also installing Erlang/OTP $rel ($build_name) ${r_type} VM in $absdir..." cd "$ERL_TOP" || exit 1 if ! TYPE="$r_type" ERL_TOP="$ERL_TOP" ./otp_build release -a "$absdir" >> "$LOGFILE" 2>&1; then show_logfile "Install Erlang/OTP $rel ($build_name) of type $r_type in $absdir failed" "$LOGFILE" exit 1 fi ;; *) BEAM_TYPE_SMP=$(\find . -name "beam.${r_type}.smp") if [ -n "$BEAM_TYPE_SMP" ]; then ERL_CHILD_SETUP_TYPE=$(\find . -name "erl_child_setup.${r_type}") l=n stderr "Also installing Erlang/OTP $rel ($build_name) ${r_type} VM in $absdir..." cp "$BEAM_TYPE_SMP" "$absdir"/erts-*/bin/ cp "$ERL_CHILD_SETUP_TYPE" "$absdir"/erts-*/bin/ fi ;; esac done [ -n "$KERL_RELEASE_TARGET" ] && [ -f bin/cerl ] && cp bin/cerl "$absdir/bin" list_add installations "$build_name $absdir"; emit_activate "$rel" "$build_name" "$absdir" >"$absdir"/activate emit_activate_fish "$rel" "$build_name" "$absdir" >"$absdir"/activate.fish emit_activate_csh "$rel" "$build_name" "$absdir" >"$absdir"/activate.csh if [ -n "$KERL_BUILD_DOCS" ]; then if ! (ERL_TOP="$ERL_TOP" make release_docs "DOC_TARGETS=$KERL_DOC_TARGETS" "RELEASE_ROOT=$absdir" >> "$LOGFILE" 2>&1); then show_logfile "Couldn't install docs for Erlang/OTP $rel ($build_name) in $absdir" "$LOGFILE" exit 1 fi else if [ "$KERL_BUILD_BACKEND" = 'tarball' ]; then if [ "$rel" != 'git' ]; then if [ -n "$KERL_INSTALL_MANPAGES" ]; then l=n stderr 'Fetching and installing manpages...' download_manpages "$rel" fi if [ -n "$KERL_INSTALL_HTMLDOCS" ]; then l=n stderr 'Fetching and installing HTML docs...' download_htmldocs "$rel" fi fi fi fi KERL_CONFIG_STORAGE_PATH="$KERL_BUILD_DIR/$build_name/otp_src_$rel/$KERL_CONFIG_STORAGE_FILENAME" [ -e "$KERL_CONFIG_STORAGE_PATH" ] && cp "$KERL_CONFIG_STORAGE_PATH" "$absdir/$KERL_CONFIG_STORAGE_FILENAME" if [ -n "$KERL_BUILD_PLT" ]; then l=n stderr 'Building Dialyzer PLT...' build_plt "$absdir" fi PID=$$ if command -v apk >/dev/null 2>&1; then # Running on Alpine Linux, assuming non-exotic shell SHELL_SUFFIX='' elif [ "$(\ps -p $PID -o ppid=)" -eq 0 ]; then SHELL_SUFFIX='' else PARENT_PID=$(\ps -p $PID -o ppid=) || exit 1 # shellcheck disable=SC2086 PARENT_CMD=$(\ps -p $PARENT_PID -o ucomm | tail -n 1) case "$PARENT_CMD" in fish) SHELL_SUFFIX='.fish' ;; csh) SHELL_SUFFIX='.csh' ;; *) SHELL_SUFFIX='' ;; esac fi l=t stderr 'You can activate this installation running the following command:' echo ". $absdir/activate$SHELL_SUFFIX" l=t stderr 'Later on, you can leave the installation typing:' l=t stderr 'kerl_deactivate' } download_manpages() { FILENAME=otp_doc_man_$1.tar.gz tarball_download "$FILENAME" l=n stderr 'Extracting manpages' cd "$absdir" && tar xzf "$KERL_DOWNLOAD_DIR/$FILENAME" } download_htmldocs() { FILENAME=otp_doc_html_"$1".tar.gz tarball_download "$FILENAME" l=n stderr 'Extracting HTML docs' (cd "$absdir" && mkdir -p html && tar -C "$absdir"/html -xzf "$KERL_DOWNLOAD_DIR/$FILENAME") } build_plt() { dialyzerd="$1"/dialyzer mkdir -p "$dialyzerd" || exit 1 plt="$dialyzerd"/plt build_log="$dialyzerd"/build.log dirs=$(\find "$1"/lib -maxdepth 2 -name ebin -type d -exec dirname {} \;) apps=$(for app in $dirs; do basename "$app" | cut -d- -f1 ; done | \grep -Ev 'erl_interface|jinterface' | xargs echo) # shellcheck disable=SC2086 "$1"/bin/dialyzer --output_plt "$plt" --build_plt --apps $apps >>"$build_log" 2>&1 status=$? if [ $status -eq 0 ] || [ $status -eq 2 ]; then l=s stderr "Done building $plt" return 0 else l=e stderr "Error building PLT, see $build_log for details" return 1 fi } do_plt() { ACTIVE_PATH="$1" if [ -n "$ACTIVE_PATH" ]; then plt="$ACTIVE_PATH"/dialyzer/plt if [ -f "$plt" ]; then l=n stderr 'Dialyzer PLT for the active installation is:' stderr "$plt" return 0 else l=e stderr 'There is no Dialyzer PLT for the active installation' return 1 fi else l=e stderr 'No Erlang/OTP installation is currently active' return 2 fi } print_buildopts() { buildopts="$1/$KERL_CONFIG_STORAGE_FILENAME" if [ -f "$buildopts" ]; then l=n stderr 'The build options for the active installation are:' cat "$buildopts" return 0 else l=e stderr 'The build options for the active installation are not available.' return 1 fi } do_deploy() { if [ -z "$1" ]; then l=e stderr 'No host given' exit 1 fi host="$1" assert_valid_installation "$2" rel="$(get_name_from_install_path "$2")" path="$2" remotepath="$path" if [ -n "$3" ]; then remotepath="$3" fi # shellcheck disable=SC2086 if ! ssh $KERL_DEPLOY_SSH_OPTIONS "$host" true >/dev/null 2>&1; then l=e stderr "Couldn't ssh to $host" exit 1 fi l=n stderr "Cloning Erlang/OTP $rel ($path) to $host ($remotepath) ..." # shellcheck disable=SC2086 if ! rsync -aqz -e "ssh $KERL_DEPLOY_SSH_OPTIONS" $KERL_DEPLOY_RSYNC_OPTIONS "$path/" "$host:$remotepath/"; then l=e stderr "Couldn't rsync Erlang/OTP $rel ($path) to $host ($remotepath)" exit 1 fi # shellcheck disable=SC2086,SC2029 if ! ssh $KERL_DEPLOY_SSH_OPTIONS "$host" "cd \"$remotepath\" && env ERL_TOP=\"\$(pwd)\" ./Install $INSTALL_OPT \"\$(pwd)\" >/dev/null 2>&1"; then l=e stderr "Couldn't install Erlang/OTP $rel to $host ($remotepath)" exit 1 fi # shellcheck disable=SC2086,SC2029 if ! ssh $KERL_DEPLOY_SSH_OPTIONS "$host" "cd \"$remotepath\" && sed -i -e \"s#$path#\"\$(pwd)\"#g\" activate"; then l=e stderr "Couldn't completely install Erlang/OTP $rel to $host ($remotepath)" exit 1 fi l=t stderr "On $host, you can activate this installation running the following command:" stderr ". $remotepath/activate" l=t stderr 'Later on, you can leave the installation typing:' stderr 'kerl_deactivate' } # Quoted from https://github.com/mkropat/sh-realpath # LICENSE: MIT realpath() { canonicalize_path "$(resolve_symlinks "$1")" } resolve_symlinks() { _resolve_symlinks "$1" } _resolve_symlinks() { _assert_no_path_cycles "$@" || return if path=$(readlink -- "$1"); then dir_context=$(dirname -- "$1") _resolve_symlinks "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@" else printf '%s\n' "$1" fi } _prepend_dir_context_if_necessary() { if [ "$1" = . ]; then printf '%s\n' "$2" else _prepend_path_if_relative "$1" "$2" fi } _prepend_path_if_relative() { case "$2" in /* ) printf '%s\n' "$2" ;; * ) printf '%s\n' "$1/$2" ;; esac } _assert_no_path_cycles() { target=$1 shift for path in "$@"; do if [ "$path" = "$target" ]; then return 1 fi done } canonicalize_path() { if [ -d "$1" ]; then _canonicalize_dir_path "$1" else _canonicalize_file_path "$1" fi } _canonicalize_dir_path() { (cd "$1" 2>/dev/null && pwd -P) } _canonicalize_file_path() { dir=$(dirname -- "$1") file=$(basename -- "$1") (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file") } # END QUOTE is_valid_install_path() { # don't allow installs into .erlang because # it's a special configuration file location for OTP if [ "$(basename -- "$1")" = '.erlang' ]; then l=e stderr 'ERROR: You cannot install a build into .erlang. (It is a special configuration file location for OTP.)' return 1 fi candidate=$(realpath "$1") canonical_home=$(realpath "$HOME") canonical_base_dir=$(realpath "$KERL_BASE_DIR") # don't allow installs into home directory if [ "$candidate" = "$canonical_home" ]; then l=e stderr "ERROR: You cannot install a build into $HOME. It's a really bad idea." return 1 fi # don't install into our base directory either. if [ "$candidate" = "$canonical_base_dir" ]; then l=e stderr "ERROR: You cannot install a build into $KERL_BASE_DIR." return 1 fi INSTALLED_NAME=$(get_name_from_install_path "$candidate") if [ -n "$INSTALLED_NAME" ]; then l=e stderr "ERROR: Installation ($INSTALLED_NAME) already registered for this location ($1)" return 1 fi # if the install directory exists, # do not allow installs into a directory that is not empty if [ -e "$1" ]; then if [ ! -d "$1" ]; then l=e stderr "ERROR: $1 is not a directory." return 1 else count=$(\find "$1" | wc -l) if [ "$count" -ne 1 ]; then l=e stderr "ERROR: $1 does not appear to be an empty directory." return 1 fi fi fi return 0 } maybe_remove() { candidate=$(realpath "$1") canonical_home=$(realpath "$HOME") if [ "$candidate" = "$canonical_home" ]; then l=w stderr "WARNING: You cannot remove an install from $HOME; it's your home directory." return 0 fi ACTIVE_PATH="$(get_active_path)" if [ "$candidate" = "$ACTIVE_PATH" ]; then l=e stderr 'ERROR: You cannot delete the active installation. Deactivate it first.' exit 1 fi rm -Rf "$1" } list_print() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then if [ "$(\wc -l "$KERL_BASE_DIR/otp_$1")" != '0' ]; then cat "$KERL_BASE_DIR/otp_$1" return 0 fi fi l=n stderr "There are no $1 available" } list_add() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then while read -r l; do if [ "$l" = "$2" ]; then return 1 fi done <"$KERL_BASE_DIR/otp_$1" echo "$2" >>"$KERL_BASE_DIR/otp_$1" || exit 1 else echo "$2" >"$KERL_BASE_DIR/otp_$1" || exit 1 fi } list_remove() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then sed $SED_OPT -i -e "/^.*$2$/d" "$KERL_BASE_DIR/otp_$1" || exit 1 fi } list_has() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then \grep "$2" "$KERL_BASE_DIR/otp_$1" >/dev/null 2>&1 && return 0 fi return 1 } path_usage() { stderr "usage: $0 path []" } list_usage() { stderr "usage: $0 list " } delete_usage() { stderr "usage: $0 delete " } cleanup_usage() { stderr "usage: $0 cleanup " } update_usage() { stderr "usage: $0 update releases" } upgrade_usage() { stderr "usage: $0 upgrade" } emit_activate_usage() { stderr "usage: $0 emit-activate [sh|bash|fish|csh]" } get_active_path() { if [ -n "$_KERL_ACTIVE_DIR" ]; then echo "$_KERL_ACTIVE_DIR" fi return 0 } get_name_from_install_path() { if [ -f "$KERL_BASE_DIR"/otp_installations ]; then \grep -m1 -E "$1$" "$KERL_BASE_DIR"/otp_installations | cut -d' ' -f1 fi return 0 } get_install_path_from_name() { if [ -f "$KERL_BASE_DIR"/otp_installations ]; then \grep -m1 -E "$1$" "$KERL_BASE_DIR"/otp_installations | cut -d' ' -f2 fi return 0 } do_active() { ACTIVE_PATH="$(get_active_path)" if [ -n "$ACTIVE_PATH" ]; then l=n stderr 'The current active installation is:' echo "$ACTIVE_PATH" return 0 else l=e stderr 'No Erlang/OTP installation is currently active' return 1 fi } make_filename() { release=$(get_otp_version "$1") if [ "$release" -ge 17 ]; then echo "OTP-$1" else echo "OTP_$1" fi } download() { mkdir -p "$KERL_DOWNLOAD_DIR" || exit 1 if [ "$KERL_BUILD_BACKEND" = 'git' ]; then FILENAME=$(make_filename "$1") github_download "$1" "$FILENAME".tar.gz else FILENAME="otp_src_$1" tarball_download "$FILENAME".tar.gz fi } github_download() { tarball_file="$KERL_DOWNLOAD_DIR/$2" tarball_url="$OTP_GITHUB_URL/archive/$2" prebuilt_url="$OTP_GITHUB_URL/releases/download/OTP-$1/otp_src_$1.tar.gz" if curl --silent --location --fail -I "$prebuilt_url" > /dev/null; then tarball_url=$prebuilt_url unset KERL_USE_AUTOCONF fi # if the file doesn't exist or the file has no size if [ ! -s "$tarball_file" ]; then l=n stderr "Downloading $1 to $KERL_DOWNLOAD_DIR..." curl -f -L -o "$tarball_file" "$tarball_url" || exit 1 else # If the downloaded tarball was corrupted due to interruption while # downloading. if ! gunzip -t "$tarball_file" 2>/dev/null; then l=n stderr "$tarball_file corrupted and redownloading..." rm -rf "$tarball_file" curl -f -L -o "$tarball_file" "$tarball_url" || exit 1 fi fi } tarball_download() { if [ ! -s "$KERL_DOWNLOAD_DIR/$1" ]; then l=n stderr "Downloading $1 to $KERL_DOWNLOAD_DIR" curl -f -L -o "$KERL_DOWNLOAD_DIR/$1" "$ERLANG_DOWNLOAD_URL/$1" || exit 1 update_checksum_file fi ensure_checksum_file l=n stderr 'Verifying archive checksum...' SUM="$($MD5SUM "$KERL_DOWNLOAD_DIR/$1" | cut -d' ' -f $MD5SUM_FIELD)" ORIG_SUM="$(\grep -F "$1" "$KERL_DOWNLOAD_DIR"/MD5 | cut -d' ' -f2)" if [ "$SUM" != "$ORIG_SUM" ]; then l=e stderr "Checksum error, check the files in $KERL_DOWNLOAD_DIR" exit 1 fi l=s stderr "Checksum verified ($SUM)" } apply_solaris_networking_patch() { patch -N -p1 <<_END_PATCH --- otp-a/erts/emulator/drivers/common/inet_drv.c +++ otp-b/erts/emulator/drivers/common/inet_drv.c @@ -4166,16 +4166,7 @@ break; case INET_IFOPT_HWADDR: { -#ifdef SIOCGIFHWADDR - if (ioctl(desc->s, SIOCGIFHWADDR, (char *)&ifreq) < 0) - break; - buf_check(sptr, s_end, 1+2+IFHWADDRLEN); - *sptr++ = INET_IFOPT_HWADDR; - put_int16(IFHWADDRLEN, sptr); sptr += 2; - /* raw memcpy (fix include autoconf later) */ - sys_memcpy(sptr, (char*)(&ifreq.ifr_hwaddr.sa_data), IFHWADDRLEN); - sptr += IFHWADDRLEN; -#elif defined(SIOCGENADDR) +#if defined(SIOCGENADDR) if (ioctl(desc->s, SIOCGENADDR, (char *)&ifreq) < 0) break; buf_check(sptr, s_end, 1+2+sizeof(ifreq.ifr_enaddr)); _END_PATCH } apply_darwin_compiler_patch() { patch -N -p0 <<_END_PATCH --- erts/emulator/beam/beam_bp.c.orig 2011-10-03 13:12:07.000000000 -0500 +++ erts/emulator/beam/beam_bp.c 2013-10-04 13:42:03.000000000 -0500 @@ -496,7 +496,8 @@ } /* bp_hash */ -ERTS_INLINE Uint bp_sched2ix() { +#ifndef ERTS_DO_INCL_GLB_INLINE_FUNC_DEF +ERTS_GLB_INLINE Uint bp_sched2ix() { #ifdef ERTS_SMP ErtsSchedulerData *esdp; esdp = erts_get_scheduler_data(); @@ -505,6 +506,7 @@ return 0; #endif } +#endif static void bp_hash_init(bp_time_hash_t *hash, Uint n) { Uint size = sizeof(bp_data_time_item_t)*n; Uint i; --- erts/emulator/beam/beam_bp.h.orig 2011-10-03 13:12:07.000000000 -0500 +++ erts/emulator/beam/beam_bp.h 2013-10-04 13:42:08.000000000 -0500 @@ -144,7 +144,19 @@ #define ErtsSmpBPUnlock(BDC) #endif -ERTS_INLINE Uint bp_sched2ix(void); +ERTS_GLB_INLINE Uint bp_sched2ix(void); + +#ifdef ERTS_DO_INCL_GLB_INLINE_FUNC_DEF +ERTS_GLB_INLINE Uint bp_sched2ix() { +#ifdef ERTS_SMP + ErtsSchedulerData *esdp; + esdp = erts_get_scheduler_data(); + return esdp->no - 1; +#else + return 0; +#endif +} +#endif #ifdef ERTS_SMP #define bp_sched2ix_proc(p) ((p)->scheduler_data->no - 1) _END_PATCH } # javadoc 8 includes always-enabled document linting which causes # documentation builds to fail on older OTP releases. apply_javadoc_linting_patch() { # The _END_PATCH token is quoted below to disable parameter substitution patch -N -p0 <<'_END_PATCH' --- lib/jinterface/doc/src/Makefile.orig 2016-05-23 14:34:48.000000000 -0500 +++ lib/jinterface/doc/src/Makefile 2016-05-23 14:35:48.000000000 -0500 @@ -142,7 +142,7 @@ rm -f errs core *~ jdoc:$(JAVA_SRC_FILES) - (cd ../../java_src;$(JAVADOC) -sourcepath . -d $(JAVADOC_DEST) \ + (cd ../../java_src;$(JAVADOC) -Xdoclint:none -sourcepath . -d $(JAVADOC_DEST) \ -windowtitle $(JAVADOC_TITLE) $(JAVADOC_PKGS)) man: _END_PATCH } # perl 5.24 fatalizes the warning this causes apply_r14_beam_makeops_patch() { patch -N -p0 <<'_END_PATCH' --- erts/emulator/utils/beam_makeops.orig 2016-05-23 21:40:42.000000000 -0500 +++ erts/emulator/utils/beam_makeops 2016-05-23 21:41:08.000000000 -0500 @@ -1576,7 +1576,7 @@ if $min_window{$key} > $min_window; pop(@{$gen_transform{$key}}) - if defined @{$gen_transform{$key}}; # Fail + if defined $gen_transform{$key}; # Fail my(@prefix) = (&make_op($comment), &make_op('', 'try_me_else', &tr_code_len(@code))); unshift(@code, @prefix); push(@{$gen_transform{$key}}, @code, &make_op('', 'fail')); _END_PATCH } # https://github.com/erlang/otp/commit/21ca6d3a137034f19862db769a5b7f1c5528dbc4.diff apply_r15_beam_makeops_patch() { patch -N -p1 <<'_END_PATCH' --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1711,7 +1711,7 @@ sub tr_gen_to { my $prev_last; $prev_last = pop(@{$gen_transform{$key}}) - if defined @{$gen_transform{$key}}; # Fail + if defined $gen_transform{$key}; # Fail if ($prev_last && !is_instr($prev_last, 'fail')) { error("Line $line: A previous transformation shadows '$orig_transform'"); _END_PATCH } #https://github.com/erlang/otp/commit/a64c4d806fa54848c35632114585ad82b98712e8.diff apply_wx_ptr_patch() { patch -N -p1 <<'_END_PATCH' diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index 0d2da5d4a79..8118136d30e 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -666,7 +666,7 @@ void * WxeApp::getPtr(char * bp, wxeMemEnv *memenv) { throw wxe_badarg(index); } void * temp = memenv->ref2ptr[index]; - if((index < memenv->next) && ((index == 0) || (temp > NULL))) + if((index < memenv->next) && ((index == 0) || (temp != (void *)NULL))) return temp; else { throw wxe_badarg(index); @@ -678,7 +678,7 @@ void WxeApp::registerPid(char * bp, ErlDrvTermData pid, wxeMemEnv * memenv) { if(!memenv) throw wxe_badarg(index); void * temp = memenv->ref2ptr[index]; - if((index < memenv->next) && ((index == 0) || (temp > NULL))) { + if((index < memenv->next) && ((index == 0) || (temp != (void *) NULL))) { ptrMap::iterator it; it = ptr2ref.find(temp); if(it != ptr2ref.end()) { _END_PATCH } apply_r16_wx_ptr_patch() { patch -N -p1 <<'_END_PATCH' diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index cc9bcc995..1b1912630 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -757,7 +757,7 @@ void * WxeApp::getPtr(char * bp, wxeMemEnv *memenv) { throw wxe_badarg(index); } void * temp = memenv->ref2ptr[index]; - if((index < memenv->next) && ((index == 0) || (temp > NULL))) + if((index < memenv->next) && ((index == 0) || (temp != (void *)NULL))) return temp; else { throw wxe_badarg(index); @@ -769,7 +769,7 @@ void WxeApp::registerPid(char * bp, ErlDrvTermData pid, wxeMemEnv * memenv) { if(!memenv) throw wxe_badarg(index); void * temp = memenv->ref2ptr[index]; - if((index < memenv->next) && ((index == 0) || (temp > NULL))) { + if((index < memenv->next) && ((index == 0) || (temp != (void *)NULL))) { ptrMap::iterator it; it = ptr2ref.find(temp); if(it != ptr2ref.end()) { _END_PATCH } # https://github.com/erlang/otp/commit/e27119948fc6ab28bea81019720bddaac5b655a7.patch apply_zlib_patch() { patch -N -p1 <<'_END_PATCH' diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 656de7c49ad..4491d486837 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1193,6 +1193,7 @@ typedef struct B2TContext_t { } u; } B2TContext; +static B2TContext* b2t_export_context(Process*, B2TContext* src); static uLongf binary2term_uncomp_size(byte* data, Sint size) { @@ -1225,7 +1226,7 @@ static uLongf binary2term_uncomp_size(byte* data, Sint size) static ERTS_INLINE int binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size, - B2TContext* ctx) + B2TContext** ctxp, Process* p) { byte *bytes = data; Sint size = data_size; @@ -1239,8 +1240,8 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size, size--; if (size < 5 || *bytes != COMPRESSED) { state->extp = bytes; - if (ctx) - ctx->state = B2TSizeInit; + if (ctxp) + (*ctxp)->state = B2TSizeInit; } else { uLongf dest_len = (Uint32) get_int32(bytes+1); @@ -1257,16 +1258,26 @@ binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size, return -1; } state->extp = erts_alloc(ERTS_ALC_T_EXT_TERM_DATA, dest_len); - ctx->reds -= dest_len; + if (ctxp) + (*ctxp)->reds -= dest_len; } state->exttmp = 1; - if (ctx) { + if (ctxp) { + /* + * Start decompression by exporting trap context + * so we don't have to deal with deep-copying z_stream. + */ + B2TContext* ctx = b2t_export_context(p, *ctxp); + ASSERT(state = &(*ctxp)->b2ts); + state = &ctx->b2ts; + if (erl_zlib_inflate_start(&ctx->u.uc.stream, bytes, size) != Z_OK) return -1; ctx->u.uc.dbytes = state->extp; ctx->u.uc.dleft = dest_len; ctx->state = B2TUncompressChunk; + *ctxp = ctx; } else { uLongf dlen = dest_len; @@ -1308,7 +1319,7 @@ erts_binary2term_prepare(ErtsBinary2TermState *state, byte *data, Sint data_size { Sint res; - if (binary2term_prepare(state, data, data_size, NULL) < 0 || + if (binary2term_prepare(state, data, data_size, NULL, NULL) < 0 || (res=decoded_size(state->extp, state->extp + state->extsize, 0, NULL)) < 0) { if (state->exttmp) @@ -1435,7 +1446,7 @@ static Eterm binary_to_term_int(Process* p, Uint32 flags, Eterm bin, Binary* con if (ctx->aligned_alloc) { ctx->reds -= bin_size / 8; } - if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, ctx) < 0) { + if (binary2term_prepare(&ctx->b2ts, bytes, bin_size, &ctx, p) < 0) { ctx->state = B2TBadArg; } break; _END_PATCH } # https://github.com/erlang/otp/commit/4b0467c.patch apply_in6addr_test_patch() { patch -N -p1 <<'_END_PATCH' diff --git a/erts/configure.in b/erts/configure.in index caa1ce568b..6ebb3d3a25 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2191,6 +2191,7 @@ AC_CACHE_CHECK( #include #include #include + #include ]], [[printf("%d", in6addr_any.s6_addr[16]);]] )], @@ -2214,6 +2215,7 @@ AC_CACHE_CHECK( #include #include #include + #include ]], [[printf("%d", in6addr_loopback.s6_addr[16]);]] )], _END_PATCH } upgrade_kerl() { install_folder=$1 curl -s -o "$install_folder/kerl" $KERL_GIT_BASE/kerl chmod +x "$install_folder/kerl" version=$(kerl version) current_kerl_path="$(which kerl)" l=n stderr "kerl $version is now available at $current_kerl_path." } case "$1" in version) echo "$KERL_VERSION" exit 0 ;; build) if [ "$2" = 'git' ]; then if [ $# -ne 5 ]; then l=e stderr "usage: $0 $1 $2 " exit 1 fi do_git_build "$3" "$4" "$5" else if [ $# -eq 2 ]; then do_normal_build "$2" "$2" elif [ $# -eq 3 ]; then do_normal_build "$2" "$3" else l=e stderr "usage: $0 $1 " exit 1 fi fi ;; build-install) # naming contains _or_ because do_build_install either accepts $2 as "git" or otherwise release_or_git="$2" build_name_or_git_url="$3" directory_or_git_version="$4" build_name="$5" directory="$6" if [ "$release_or_git" = "git" ]; then if [ $# -ne 5 ] && [ $# -ne 6 ]; then l=e stderr "usage: $0 $1 git [directory]" exit 1 fi git_url="$build_name_or_git_url" git_version="$directory_or_git_version" else if [ $# -ne 2 ] && [ $# -ne 3 ] && [ $# -ne 4 ]; then l=e stderr "usage: $0 $1 [build_name] [directory]" exit 1 fi release="$release_or_git" build_name="$build_name_or_git_url" git_url="_unused_" directory="$directory_or_git_version" git_version="_unused_" if [ $# -eq 2 ]; then build_name="$release" fi fi if [ -z "$directory" ]; then if [ -z "$KERL_DEFAULT_INSTALL_DIR" ]; then directory="$PWD" else directory="$KERL_DEFAULT_INSTALL_DIR/$build_name" fi fi do_build_install "$release_or_git" "$git_url" "$git_version" "$build_name" "$directory" ;; install) if [ $# -lt 2 ]; then l=e stderr "usage: $0 $1 [directory]" exit 1 fi if [ $# -eq 3 ]; then do_install "$2" "$3" else if [ -z "$KERL_DEFAULT_INSTALL_DIR" ]; then do_install "$2" "$PWD" else do_install "$2" "$KERL_DEFAULT_INSTALL_DIR/$2" fi fi ;; deploy) if [ $# -lt 2 ]; then l=e stderr "usage: $0 $1 <[user@]host> [directory] [remote_directory]" exit 1 fi if [ $# -eq 4 ]; then do_deploy "$2" "$3" "$4" else if [ $# -eq 3 ]; then do_deploy "$2" "$3" else do_deploy "$2" . fi fi ;; update) if [ $# -lt 2 ]; then update_usage exit 1 fi case "$2" in releases) rm -f "${KERL_BASE_DIR:?}"/otp_releases if check_releases; then l=n stderr 'The available releases are:' list_print releases else l=e stderr 'Check releases failed' exit 1 fi ;; *) update_usage exit 1 ;; esac ;; upgrade) if [ $# -ne 1 ]; then upgrade_usage exit 1 fi current_kerl_path="$(which kerl)" which_status=$? if [ $which_status != 0 ]; then if [ -z ${KERL_APP_INSTALL_DIR+x} ]; then install_folder="$PWD" else install_folder="$KERL_APP_INSTALL_DIR" fi l=n stderr "kerl not installed. Dropping it into $install_folder/kerl..." upgrade_kerl "$install_folder" else version=$(kerl version) l=n stderr "local kerl found ($current_kerl_path) at version $version." latest=$(curl -s $KERL_GIT_BASE/LATEST) l=n stderr "remote kerl found at version $latest." if [ "$version" != "$latest" ]; then l=w stderr "Versions are different. Upgrading to $latest." current_kerl_path=$(dirname "$current_kerl_path") upgrade_kerl "$current_kerl_path" else echo "No upgrade required." fi l=n stderr "Updating list of available releases... " kerl update releases > /dev/null l=s stderr "Done!" fi ;; list) if [ $# -ne 2 ]; then list_usage exit 1 fi case "$2" in releases) check_releases list_print "$2" l=t stderr "Run '$0 update releases' to update this list from erlang.org" ;; builds) list_print "$2" ;; installations) list_print "$2" ;; *) l=e stderr "Cannot list $2" list_usage exit 1 ;; esac ;; path) # Usage: # kerl path # # Print currently active installation path, else non-zero exit # kerl path # Print path to installation with name , else non-zero exit if [ -z "$2" ]; then activepath=$(get_active_path) if [ -z "$activepath" ]; then l=e stderr 'No active kerl-managed erlang installation' exit 1 fi echo "$activepath" else # There are some possible extensions to this we could # consider, such as: # - if 2+ matches: prefer one in a subdir from $PWD # - prefer $KERL_DEFAULT_INSTALL_DIR match= for ins in $(list_print installations | cut -d' ' -f2); do if [ "$(basename "$ins")" = "$2" ]; then if [ -z "$match" ]; then match="$ins" else l=e stderr 'Error: too many matching installations' >&2 exit 2 fi fi done [ -n "$match" ] && echo "$match" && exit 0 l=e stderr 'Error: no matching installation found' >&2 && exit 1 fi ;; delete) if [ $# -ne 3 ]; then delete_usage exit 1 fi case "$2" in build) rel="$(get_release_from_name "$3")" if [ -d "${KERL_BUILD_DIR:?}/$3" ]; then maybe_remove "${KERL_BUILD_DIR:?}/$3" else if [ -z "$rel" ]; then l=e stderr "No build named $3" exit 1 fi fi list_remove "$2"s "$rel,$3" l=n stderr "The $3 build has been deleted" ;; installation) assert_valid_installation "$3" if [ -d "$3" ]; then maybe_remove "$3" else maybe_remove "$(get_install_path_from_name "$3")" fi escaped="$(echo "$3" | \sed $SED_OPT -e 's#/$##' -e 's#\/#\\\/#g')" list_remove "$2"s "$escaped" l=n stderr "The installation \"$3\" has been deleted" ;; *) l=e stderr "Cannot delete $2" delete_usage exit 1 ;; esac ;; active) if ! do_active; then exit 1; fi ;; plt) ACTIVE_PATH=$(get_active_path) if ! do_plt "$ACTIVE_PATH"; then exit 1; fi ;; status) l=n stderr 'Available builds:' list_print builds echo '----------' l=n stderr 'Available installations:' list_print installations echo '----------' if do_active; then ACTIVE_PATH=$(get_active_path) if [ -n "$ACTIVE_PATH" ]; then do_plt "$ACTIVE_PATH" print_buildopts "$ACTIVE_PATH" else l=e stderr 'No Erlang/OTP installation is currently active' exit 1 fi fi exit 0 ;; prompt) FMT=' (%s)' if [ -n "$2" ]; then FMT="$2" fi ACTIVE_PATH="$(get_active_path)" if [ -n "$ACTIVE_PATH" ]; then ACTIVE_NAME="$(get_name_from_install_path "$ACTIVE_PATH")" if [ -z "$ACTIVE_NAME" ]; then VALUE="$(basename "$ACTIVE_PATH")*" else VALUE="$ACTIVE_NAME" fi # shellcheck disable=SC2059 printf "$FMT" "$VALUE" fi exit 0 ;; cleanup) if [ $# -ne 2 ]; then cleanup_usage exit 1 fi case "$2" in all) l=n stderr 'Cleaning up compilation products for ALL builds' rm -rf "${KERL_BUILD_DIR:?}"/* rm -rf "${KERL_DOWNLOAD_DIR:?}"/* rm -rf "${KERL_GIT_DIR:?}"/* l=n stderr "Cleaned up all compilation products under $KERL_BUILD_DIR" ;; *) l=n stderr "Cleaning up compilation products for $2" rm -rf "${KERL_BUILD_DIR:?}/$2" l=n stderr "Cleaned up compilation products for $2 under $KERL_BUILD_DIR" ;; esac ;; emit-activate) release=$2 build_name=$3 directory=$4 shell=$5 if [ $# -ne 4 ] && [ $# -ne 5 ]; then emit_activate_usage exit 1 fi if [ -z "$5" ]; then shell="sh" elif [ "$shell" = "bash" ]; then shell="sh" fi case "$shell" in sh) emit_activate "$release" "$build_name" "$directory" ;; fish) emit_activate_fish "$release" "$build_name" "$directory" ;; csh) emit_activate_csh "$release" "$build_name" "$directory" ;; *) emit_activate_usage exit 1 esac ;; *) l=e stderr "unknown command: $1"; usage ;; esac