#! /bin/sh # Copyright (c) 2011 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. ERLANG_DOWNLOAD_URL=http://www.erlang.org/download KERL_BASE_DIR="$HOME/.kerl" KERL_CONFIG="$HOME/.kerlrc" KERL_DOWNLOAD_DIR="$KERL_BASE_DIR/archives" KERL_BUILD_DIR="$KERL_BASE_DIR/builds" if [ -n "$KERL_CONFIGURE_OPTIONS" ]; then _KCO="$KERL_CONFIGURE_OPTIONS" fi if [ -n "$KERL_SASL_STARTUP" ]; then _KSS="$KERL_SASL_STARTUP" fi if [ -n "$KERL_AGNER_AUTOINSTALL" ]; then _KAA="$KERL_AGNER_AUTOINSTALL" fi KERL_CONFIGURE_OPTIONS= KERL_DISABLE_AGNER= KERL_SASL_STARTUP= # ensure the base dir exsists mkdir -p "$KERL_BASE_DIR" # source the config file if available if [ -f "$KERL_CONFIG" ]; then . "$KERL_CONFIG"; fi if [ -n "$_KCO" ]; then KERL_CONFIGURE_OPTIONS="$_KCO" fi if [ -n "$_KSS" ]; then KERL_SASL_STARTUP="$_KSS" fi if [ -n "$_KAA" ]; then KERL_AGNER_AUTOINSTALL="$_KAA" fi if [ -z "$KERL_SASL_STARTUP" ]; then INSTALL_OPT=-minimal else INSTALL_OPT=-sasl fi KERL_SYSTEM=`uname -s` case "$KERL_SYSTEM" in Darwin) MD5SUM="openssl md5" MD5SUM_FIELD=2 SED_OPT=-E if [ `gcc --version | grep llvm | wc -l` = "1" ]; then if [ -x `which gcc-4.2` ]; then echo "Adjust compiler settings for OS X: using gcc-4.2" KERL_CONFIGURE_OPTIONS="CC=gcc-4.2 $KERL_CONFIGURE_OPTIONS" else echo "Adjust compiler settings for OS X: using -O0" KERL_CONFIGURE_OPTIONS="CFLAGS=-O0 $KERL_CONFIGURE_OPTIONS" fi fi ;; *) MD5SUM=md5sum MD5SUM_FIELD=1 SED_OPT=-r ;; esac usage() { echo "kerl: build and install Erlang/OTP" echo "usage: $0 [options ...]" echo "\n Command to be executed\n" echo "Valid commands are:" echo " build Build specified release or git repository" echo " install Install the specified release at the given location" echo " update Update agner or the list of available releases from erlang.org" echo " list List releases, builds and installations" echo " delete Delete builds and installations" echo " active Print the path of the active installation" echo " status Print available builds and installations" exit 1 } if [ $# -eq 0 ]; then usage; fi get_releases() { curl -s $ERLANG_DOWNLOAD_URL/ | \ sed $SED_OPT -e 's/^.*>otp_src_(R1[-1234567890ABCD]+)\.tar\.gz<.*$/\1/' \ -e '/^R/!d' } update_checksum_file() { echo "Getting the checksum file from erlang.org..." curl $ERLANG_DOWNLOAD_URL/MD5 > "$KERL_DOWNLOAD_DIR/MD5" || exit 1 } ensure_checksum_file() { if [ ! -f "$KERL_DOWNLOAD_DIR/MD5" ]; then update_checksum_file fi } check_releases() { if [ ! -f "$KERL_BASE_DIR/otp_releases" ]; then echo "Getting the available releases from erlang.org..." get_releases > "$KERL_BASE_DIR/otp_releases" fi } # c.f. agner issue #98 # https://github.com/agner/agner/issues/#issue/98 KERL_NO_AGNER_SUPPORT="R10B-0 R10B-2 R10B-3 R10B-4 R10B-5 R10B-6 R10B-7 R10B-8 R10B-9 R11B-0 R11B-1 R11B-2 R11B-3 R11B-4 R11B-5 R12B-0 R12B-1 R12B-2 R12B-3 R12B-4 R12B-5 R13A R13B R13B01 R13B02 R13B03 R13B04" agner_support() { if [ -z "$KERL_DISABLE_AGNER" ]; then return 1; fi for v in $KERL_NO_AGNER_SUPPORT; do if [ "$v" = "$1" ]; then return 1 fi done return 0 } is_valid_release() { check_releases for rel in `cat $KERL_BASE_DIR/otp_releases`; do if [ "$1" = "$rel" ]; then return 0 fi done return 1 } assert_valid_release() { if ! is_valid_release $1; then echo "$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 for l in `cat "$KERL_BASE_DIR/otp_builds"`; do rel=`echo $l | cut -d "," -f 1` name=`echo $l | cut -d "," -f 2` if [ "$name" = "$1" ]; then echo "$rel" return 0 fi done fi return 1 } is_valid_installation() { if [ -f "$1/activate" ]; then return 0 fi return 1 } do_update_agner() { rel=`get_release_from_name $1` if [ "$?" -eq 1 ]; then echo "Unknown build name $1" exit 1 fi TARGET="$KERL_BUILD_DIR/$1/release_$rel" cd "$KERL_BUILD_DIR/$1/agner_$rel" && \ git pull && PATH="$KERL_BUILD_DIR/$1/otp_src_$rel/bin:$PATH" \ make > /dev/null 2>&1 if [ "$?" -eq 1 ]; then return 1 fi return 0 } assert_build_name_unused() { if [ -f "$KERL_BASE_DIR/otp_builds" ]; then for l in `cat "$KERL_BASE_DIR/otp_builds"`; do name=`echo $l | cut -d "," -f 2` if [ "$name" = "$1" ]; then echo "There's already a build named $1" exit 1 fi done fi } do_git_build() { assert_build_name_unused $3 mkdir -p "$KERL_BUILD_DIR/$3" cd "$KERL_BUILD_DIR/$3" echo "Checking Erlang/OTP git repository from $1..." git clone $1 otp_src_git > /dev/null 2>&1 if [ "$?" -eq 1 ]; then echo "Error retriving git repository" exit 1 fi if [ ! -x otp_src_git/otp_build ]; then echo "Not a valid Erlang/OTP repository" rm -Rf "$KERL_BUILD_DIR/$3" exit 1 fi cd otp_src_git git branch -a | grep "$2" > /dev/null 2>&1 if [ "$?" -eq 1 ]; then git checkout $2 > /dev/null 2>&1 else git checkout -b $2 origin/$2 > /dev/null 2>&1 fi if [ "$?" -eq 1 ]; then echo "Couldn't checkout specified version" rm -Rf "$KERL_BUILD_DIR/$3" exit 1 fi LOGFILE="$KERL_BUILD_DIR/$3/opt_build.log" echo "Building Erlang/OTP $3 from git, please wait..." ./otp_build setup -a $KERL_CONFIGURE_OPTIONS > "$LOGFILE" 2>&1 if [ "$?" -eq 1 ]; then echo "Build error, see $LOGFILE" exit 1 fi rm -f "$LOGFILE" ./otp_build release -a "$KERL_BUILD_DIR/$3/release_git" > /dev/null 2>&1 cd "$KERL_BUILD_DIR/$3/release_git" ./Install $INSTALL_OPT "$KERL_BUILD_DIR/$3/release_git" > /dev/null 2>&1 echo "Erlang/OTP $3 from git has been successfully built" list_add builds "git,$3" if [ -z "$KERL_DISABLE_AGNER" ]; then echo "Fetching and building agner..." cd "$KERL_BUILD_DIR/$3" && \ git clone https://github.com/agner/agner.git agner_git > /dev/null 2>&1 && \ cd agner_git && \ PATH="$KERL_BUILD_DIR/$3/otp_src_git/bin:$PATH" make > /dev/null 2>&1 && \ if [ "$?" -eq 1 ]; then echo "Agner install failed"; exit 1 fi echo "Agner has been successfully built" fi } do_build() { assert_valid_release $1 assert_build_name_unused $2 FILENAME=otp_src_$1.tar.gz if [ ! -f "$KERL_DOWNLOAD_DIR/$FILENAME" ]; then echo "Downloading $FILENAME to $KERL_DOWNLOAD_DIR" mkdir -p "$KERL_DOWNLOAD_DIR" curl $ERLANG_DOWNLOAD_URL/$FILENAME > "$KERL_DOWNLOAD_DIR/$FILENAME" update_checksum_file fi ensure_checksum_file echo "Verifying archive checksum..." SUM=`$MD5SUM "$KERL_DOWNLOAD_DIR/$FILENAME" | cut -d " " -f $MD5SUM_FIELD` ORIG_SUM=`grep $FILENAME "$KERL_DOWNLOAD_DIR/MD5" | cut -d " " -f 2` if [ "$SUM" != "$ORIG_SUM" ]; then echo "Checksum error, check the files in $KERL_DOWNLOAD_DIR" exit 1 fi echo "Checksum verified ($SUM)" mkdir -p "$KERL_BUILD_DIR/$2" if [ ! -d "$KERL_BUILD_DIR/$2/otp_src_$1" ]; then echo "Extracting source code" cd "$KERL_BUILD_DIR/$2" && tar xfz "$KERL_DOWNLOAD_DIR/$FILENAME" fi echo "Building Erlang/OTP $1 ($2), please wait..." ERL_TOP="$KERL_BUILD_DIR/$2/otp_src_$1" cd "$ERL_TOP" LOGFILE="$KERL_BUILD_DIR/$2/otp_build_$1.log" if [ -n "$KERL_USE_AUTOCONF" ]; then ./otp_build setup -a $KERL_CONFIGURE_OPTIONS > "$LOGFILE" 2>&1 else ./otp_build configure $KERL_CONFIGURE_OPTIONS > "$LOGFILE" 2>&1 && \ ./otp_build boot -a > "$LOGFILE" 2>&1 fi if [ "$?" -eq 1 ]; then echo "Build failed, see $LOGFILE" list_remove builds "$1 $2" exit 1 fi rm -f "$LOGFILE" ERL_TOP="$ERL_TOP" ./otp_build release -a "$KERL_BUILD_DIR/$2/release_$1" > /dev/null 2>&1 cd "$KERL_BUILD_DIR/$2/release_$1" ./Install $INSTALL_OPT "$KERL_BUILD_DIR/$2/release_$1" > /dev/null 2>&1 echo "Erlang/OTP $1 ($2) has been successfully built" list_add builds "$1,$2" if agner_support $1; then echo "Fetching and building agner..." cd "$KERL_BUILD_DIR/$2" && \ git clone https://github.com/agner/agner.git agner_$1 > /dev/null 2>&1 && \ cd agner_$1 && \ PATH="$KERL_BUILD_DIR/$2/otp_src_$1/bin:$PATH" make > /dev/null 2>&1 && \ if [ "$?" -eq 1 ]; then echo "Agner install failed"; exit 1 fi echo "Agner has been successfully built" fi } do_install() { rel=`get_release_from_name $1` if [ "$?" -eq 1 ]; then echo "No build named $1" exit 1 fi mkdir -p "$2" if [ ! -d "$2" ]; then echo "Destination is not a directory" exit 1 fi absdir=`cd "$2" && pwd` echo "Installing Erlang/OTP $rel ($1) in $absdir..." ERL_TOP="$KERL_BUILD_DIR/$1/otp_src_$rel" cd "$ERL_TOP" ERL_TOP="$ERL_TOP" ./otp_build release -a "$absdir" > /dev/null 2>&1 && cd "$absdir" && ./Install $INSTALL_OPT "$absdir" > /dev/null 2>&1 if [ $? -eq 1 ]; then echo "Couldn't install Erlang/OTP $rel ($1) in $absdir" exit 1 fi list_add installations "$1 $absdir"; cat < "$absdir/activate" # credits to virtualenv kerl_deactivate() { if [ -n "\$_KERL_SAVED_PATH" ]; then PATH="\$_KERL_SAVED_PATH" export PATH unset _KERL_SAVED_PATH fi if [ -n "\$_KERL_SAVED_AGNER_BIN" ]; then AGNER_BIN="\$_KERL_SAVED_AGNER_BIN" export AGNER_BIN unset _KERL_SAVED_AGNER_BIN fi if [ -n "\$_KERL_SAVED_AGNER_EXACT_PREFIX" ]; then AGNER_EXACT_PREFIX="\$_KERL_SAVED_AGNER_EXACT_PREFIX" export AGNER_EXACT_PREFIX unset _KERL_SAVED_AGNER_EXACT_PREFIX fi if [ -n "\$_KERL_SAVED_REBAR_PLT_DIR" ]; then REBAR_PLT_DIR="\$_KERL_SAVED_REBAR_PLT_DIR" export REBAR_PLT_DIR unset _KERL_SAVED_REBAR_PLT_DIR fi if [ -n "\$BASH" -o -n "\$ZSH_VERSION" ]; then hash -r fi if [ ! "\$1" = "nondestructive" ]; then unset -f kerl_deactivate fi } kerl_deactivate nondestructive _KERL_SAVED_PATH="\$PATH" export _KERL_SAVED_PATH _KERL_SAVED_AGNER_BIN="\$AGNER_BIN" export _KERL_SAVED_AGNER_BIN _KERL_SAVED_AGNER_EXACT_PREFIX="\$AGNER_EXACT_PREFIX" export _KERL_SAVED_AGNER_EXACT_PREFIX _KERL_SAVED_REBAR_PLT_DIR="\$REBAR_PLT_DIR" export _KERL_SAVED_REBAR_PLT_DIR PATH="$absdir/bin:\$PATH" export PATH AGNER_BIN="$absdir/bin" export AGNER_BIN AGNER_EXACT_PREFIX="$absdir/lib" export AGNER_EXACT_PREFIX REBAR_PLT_DIR="$absdir" export REBAR_PLT_DIR if [ -n "\$BASH" -o -n "\$ZSH_VERSION" ]; then hash -r fi ACTIVATE if agner_support $1; then echo "Installing agner in $absdir..." cp "$KERL_BUILD_DIR/$1/agner_$rel/agner" "$absdir/bin/" if [ -n "$KERL_INSTALL_AGNERIZED_REBAR" ]; then echo "Installing agnerized rebar in $absdir..." cp "$KERL_BUILD_DIR/$1/agner_$rel/agner" "$absdir/bin/rebar" fi if [ -n "$KERL_AGNER_AUTOINSTALL" ]; then for i in $KERL_AGNER_AUTOINSTALL; do echo "Autoinstalling $i" agner install $i > /dev/null 2>&1 done fi fi echo "You can activate this installation running the following command:" echo ". $absdir/activate" echo "Later on, you can leave the installation typing:" echo "kerl_deactivate" } list_print() { if [ -f $KERL_BASE_DIR/otp_$1 ]; then if [ "`cat "$KERL_BASE_DIR/otp_$1" | wc -l`" != "0" ]; then if [ -z "$2" ]; then cat "$KERL_BASE_DIR/otp_$1" else echo `cat "$KERL_BASE_DIR/otp_$1"` fi return 0 fi fi echo "There are no $1 available" } list_add() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then for l in `cat "$KERL_BASE_DIR/otp_$1"`; do if [ "$l" = "$2" ]; then return 1 fi done echo "$2" >> "$KERL_BASE_DIR/otp_$1" else echo "$2" > "$KERL_BASE_DIR/otp_$1" fi } list_remove() { if [ -f "$KERL_BASE_DIR/otp_$1" ]; then sed $SED_OPT -i -e "/^.*$2$/d" "$KERL_BASE_DIR/otp_$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 } list_usage() { echo "usage: $0 list " } delete_usage() { echo "usage: $0 delete " } update_usage() { echo "usage: $0 $1 " } do_active() { if [ -n "$_KERL_SAVED_PATH" ]; then echo "The current active installation is:" echo `echo $PATH | cut -d ":" -f 1 | sed 's/\(.*\)..../\1/'` return 0 else echo "No Erlang/OTP kerl installation is currently active" return 1 fi } case "$1" in build) if [ "$2" = "git" ]; then if [ $# -ne 5 ]; then echo "usage: $0 $1 $2 " exit 1 fi do_git_build $3 $4 $5 else if [ $# -lt 3 ]; then echo "usage: $0 $1 " exit 1 fi do_build $2 $3 fi ;; install) if [ $# -lt 2 ]; then echo "usage: $0 $1 [directory]" exit 1 fi if [ $# -eq 3 ]; then do_install $2 "$3" else do_install $2 . fi ;; update) if [ $# -lt 2 ]; then update_usage exit 1 fi case "$2" in releases) rm -f "$KERL_BASE_DIR/otp_releases" check_releases echo "The available releases are:" list_print releases spaces ;; agner) if [ $# -ne 3 ]; then echo "usage: $0 $1 $2 " exit 1 fi if agner_support $3; then echo "Updating agner for build $3..." if do_update_agner $3; then echo "agner has been updated successfully" else echo "failed to update agner" exit 1 fi fi ;; *) update_usage exit 1 ;; esac ;; list) if [ $# -ne 2 ]; then list_usage exit 1 fi case "$2" in releases) check_releases list_print $2 space echo "Run \"$0 update releases\" to update this list from erlang.org" ;; builds) list_print $2 ;; installations) list_print $2 ;; *) echo "Cannot list $2" list_usage exit 1 ;; esac ;; 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 rm -Rf "$KERL_BUILD_DIR/$3" list_remove $2s "$rel,$3" echo "The $3 build has been deleted" else echo "No build named $3" exit 1 fi ;; installation) if is_valid_installation "$3"; then rm -Rf "$3" escaped=`echo "$3" | sed $SED_OPT -e 's#/$##' -e 's#\/#\\\/#g'` list_remove $2s "$escaped" echo "The installation in $3 has been deleted" else echo "$3 is not a kerl-managed Erlang/OTP installation" exit 1 fi ;; *) echo "Cannot delete $2" delete_usage exit 1 ;; esac ;; active) if ! do_active; then exit 1; fi ;; status) echo "Available builds:" list_print builds echo "----------" echo "Available installations:" list_print installations echo "----------" do_active exit 0 ;; *) echo "unkwnown command: $1"; usage; exit 1 ;; esac