rbenv-ruby-build/bin/ruby-build
Sam Stephenson ebed6000e8 Verify MD5 checksums of tarballs downloaded with install_package
Checksums are optional and specified as anchors on package URLs.
2012-11-14 19:53:57 -06:00

576 lines
12 KiB
Bash
Executable file

#!/usr/bin/env bash
RUBY_BUILD_VERSION="20121110"
set -E
exec 3<&2 # preserve original stderr at fd 3
lib() {
parse_options() {
OPTIONS=()
ARGUMENTS=()
local arg option index
for arg in "$@"; do
if [ "${arg:0:1}" = "-" ]; then
if [ "${arg:1:1}" = "-" ]; then
OPTIONS[${#OPTIONS[*]}]="${arg:2}"
else
index=1
while option="${arg:$index:1}"; do
[ -n "$option" ] || break
OPTIONS[${#OPTIONS[*]}]="$option"
index=$(($index+1))
done
fi
else
ARGUMENTS[${#ARGUMENTS[*]}]="$arg"
fi
done
}
if [ "$1" == "--$FUNCNAME" ]; then
declare -f "$FUNCNAME"
echo "$FUNCNAME \"\$1\";"
exit
fi
}
lib "$1"
resolve_link() {
$(type -p greadlink readlink | head -1) "$1"
}
abs_dirname() {
local cwd="$(pwd)"
local path="$1"
while [ -n "$path" ]; do
cd "${path%/*}"
local name="${path##*/}"
path="$(resolve_link "$name" || true)"
done
pwd
cd "$cwd"
}
build_failed() {
{ echo
echo "BUILD FAILED"
echo
if ! rmdir "${BUILD_PATH}" 2>/dev/null; then
echo "Inspect or clean up the working tree at ${BUILD_PATH}"
if file_is_not_empty "$LOG_PATH"; then
echo "Results logged to ${LOG_PATH}"
echo
echo "Last 10 log lines:"
tail -n 10 "$LOG_PATH"
fi
fi
} >&3
exit 1
}
file_is_not_empty() {
local filename="$1"
local line_count="$(wc -l "$filename" 2>/dev/null || true)"
if [ -n "$line_count" ]; then
words=( $line_count )
[ "${words[0]}" -gt 0 ]
else
return 1
fi
}
install_package() {
install_package_using "tarball" 1 $*
}
install_git() {
install_package_using "git" 2 $*
}
install_svn() {
install_package_using "svn" 2 $*
}
install_package_using() {
local package_type="$1"
local package_type_nargs="$2"
local package_name="$3"
shift 3
pushd "$BUILD_PATH" >&4
"fetch_${package_type}" "$package_name" $*
shift $(($package_type_nargs))
make_package "$package_name" $*
popd >&4
echo "Installed ${package_name} to ${PREFIX_PATH}" >&2
}
make_package() {
local package_name="$1"
shift
pushd "$package_name" >&4
before_install_package "$package_name"
build_package "$package_name" $*
after_install_package "$package_name"
fix_directory_permissions
popd >&4
}
compute_md5() {
if type md5 &>/dev/null; then
md5 -q
elif type md5sum &>/dev/null; then
local output="$(md5sum -b)"
echo "${output% *}"
else
echo "error: please install \`md5sum\` and try again" >&2
exit 1
fi
}
verify_checksum() {
local filename="$1"
if [ ! -e "$filename" ]; then
return 1
fi
local expected_checksum="$2"
if [ -z "$expected_checksum" ]; then
return 0
fi
local computed_checksum="$(compute_md5 < "$filename")"
if [ -z "$computed_checksum" ]; then
return 1
fi
if [ "$expected_checksum" != "$computed_checksum" ]; then
{ echo
echo "checksum mismatch: ${filename} (file is corrupt)"
echo "expected $expected_checksum, got $computed_checksum"
echo
} >&4
return 1
fi
}
retrieve_url() {
if type curl &>/dev/null; then
curl -f "$@"
elif type wget &>/dev/null; then
wget -O- "$@"
else
echo "error: please install \`curl\` or \`wget\` and try again" >&2
exit 1
fi
}
fetch_tarball() {
local package_name="$1"
local package_url="$2"
local checksum="${package_url#*\#}"
if [ -n "$checksum" ]; then
package_url="${package_url%%#*}"
fi
local package_filename="${package_name}.tar.gz"
symlink_tarball_from_cache "$package_filename" "$checksum" ||
download_tarball "$package_url" "$package_filename" "$checksum"
{ tar xzvf "$package_filename"
rm -f "$package_filename"
} >&4 2>&1
}
symlink_tarball_from_cache() {
if [ -n "$RUBY_BUILD_CACHE_PATH" ]; then
local package_filename="$1"
local cached_package_filename="${RUBY_BUILD_CACHE_PATH}/$package_filename"
local checksum="$2"
if verify_checksum "$cached_package_filename" "$checksum"; then
ln -s "$cached_package_filename" "$package_filename" >&4 2>&1
return 0
fi
fi
return 1
}
download_tarball() {
local package_url="$1"
local package_filename="$2"
local checksum="$3"
echo "Downloading ${package_url}..." >&2
{ retrieve_url "$package_url" > "$package_filename"
verify_checksum "$package_filename" "$checksum"
} >&4 2>&1
if [ -n "$RUBY_BUILD_CACHE_PATH" ]; then
local cached_package_filename="${RUBY_BUILD_CACHE_PATH}/$package_filename"
{ mv "$package_filename" "$cached_package_filename"
ln -s "$cached_package_filename" "$package_filename"
} >&4 2>&1
fi
}
fetch_git() {
local package_name="$1"
local git_url="$2"
local git_ref="$3"
echo "Cloning ${git_url}..." >&2
if type git &>/dev/null; then
git clone --depth 1 --branch "$git_ref" "$git_url" "${package_name}" >&4 2>&1
else
echo "error: please install \`git\` and try again" >&2
exit 1
fi
}
fetch_svn() {
local package_name="$1"
local svn_url="$2"
local svn_rev="$3"
echo "Checking out ${svn_url}..." >&2
if type svn &>/dev/null; then
svn co -r "$svn_rev" "$svn_url" "${package_name}" >&4 2>&1
else
echo "error: please install \`svn\` and try again" >&2
exit 1
fi
}
build_package() {
local package_name="$1"
shift
if [ "$#" -eq 0 ]; then
local commands="standard"
else
local commands="$*"
fi
echo "Installing ${package_name}..." >&2
for command in $commands; do
"build_package_${command}"
done
}
build_package_standard() {
local package_name="$1"
if [ "${MAKEOPTS+defined}" ]; then
MAKE_OPTS="$MAKEOPTS"
elif [ -z "${MAKE_OPTS+defined}" ]; then
MAKE_OPTS="-j 2"
fi
{ ./configure --prefix="$PREFIX_PATH" $CONFIGURE_OPTS
make $MAKE_OPTS
make install
} >&4 2>&1
}
build_package_autoconf() {
{ autoconf
} >&4 2>&1
}
build_package_ruby() {
local package_name="$1"
{ "$RUBY_BIN" setup.rb
} >&4 2>&1
}
build_package_ree_installer() {
local options=""
if [[ "Darwin" = "$(uname)" ]]; then
options="--no-tcmalloc"
fi
# Work around install_useful_libraries crash with --dont-install-useful-gems
mkdir -p "$PREFIX_PATH/lib/ruby/gems/1.8/gems"
{ ./installer --auto "$PREFIX_PATH" --dont-install-useful-gems $options $CONFIGURE_OPTS
} >&4 2>&1
}
build_package_rbx() {
local package_name="$1"
{ ./configure --prefix="$PREFIX_PATH" --gemsdir="$PREFIX_PATH"
rake install
} >&4 2>&1
}
build_package_maglev() {
build_package_copy
{ cd "${PREFIX_PATH}"
./install.sh
cd "${PREFIX_PATH}/bin"
echo "Creating symlink for ruby*"
ln -fs maglev-ruby ruby
echo "Creating symlink for irb*"
ln -fs maglev-irb irb
} >&4 2>&1
echo
echo "Run 'maglev start' to start up the stone before using 'ruby' or 'irb'"
}
build_package_jruby() {
build_package_copy
cd "${PREFIX_PATH}/bin"
ln -fs jruby ruby
install_jruby_launcher
remove_windows_files
}
install_jruby_launcher() {
cd "${PREFIX_PATH}/bin"
{ ./ruby gem install jruby-launcher
} >&4 2>&1
}
remove_windows_files() {
cd "$PREFIX_PATH"
rm -f bin/*.exe bin/*.dll bin/*.bat bin/jruby.sh
}
build_package_copy() {
mkdir -p "$PREFIX_PATH"
cp -R . "$PREFIX_PATH"
}
before_install_package() {
local stub=1
}
after_install_package() {
local stub=1
}
fix_directory_permissions() {
# Ensure installed directories are not world-writable to avoid Bundler warnings
find "$PREFIX_PATH" -type d -exec chmod go-w {} \;
}
require_gcc() {
local gcc="$(locate_gcc || true)"
if [ -z "$gcc" ]; then
local esc=$'\033'
{ echo
echo "${esc}[1mERROR${esc}[0m: This package must be compiled with GCC, but ruby-build couldn't"
echo "find a suitable \`gcc\` executable on your system. Please install GCC"
echo "and try again."
echo
if [ "$(uname -s)" = "Darwin" ]; then
echo "${esc}[1mDETAILS${esc}[0m: Apple no longer includes the official GCC compiler with Xcode"
echo "as of version 4.2. Instead, the \`gcc\` executable is a symlink to"
echo "\`llvm-gcc\`, a modified version of GCC which outputs LLVM bytecode."
echo
echo "For most programs the \`llvm-gcc\` compiler works fine. However,"
echo "versions of Ruby older than 1.9.3-p125 are incompatible with"
echo "\`llvm-gcc\`. To build older versions of Ruby you must have the official"
echo "GCC compiler installed on your system."
echo
echo "${esc}[1mTO FIX THE PROBLEM${esc}[0m: Install the official GCC compiler using these"
echo "packages: ${esc}[4mhttps://github.com/kennethreitz/osx-gcc-installer/downloads${esc}[0m"
echo
echo "You will need to install the official GCC compiler to build older"
echo "versions of Ruby even if you have installed Apple's Command Line Tools"
echo "for Xcode package. The Command Line Tools for Xcode package only"
echo "includes \`llvm-gcc\`."
fi
} >&3
return 1
fi
export CC="$gcc"
}
locate_gcc() {
local gcc gccs
IFS=: gccs=($(gccs_in_path))
verify_gcc "$CC" ||
verify_gcc "$(command -v gcc || true)" || {
for gcc in "${gccs[@]}"; do
verify_gcc "$gcc" && break || true
done
}
return 1
}
gccs_in_path() {
local gcc path paths
local gccs=()
IFS=: paths=($PATH)
shopt -s nullglob
for path in "${paths[@]}"; do
for gcc in "$path"/gcc-*; do
gccs["${#gccs[@]}"]="$gcc"
done
done
shopt -u nullglob
printf :%s "${gccs[@]}"
}
verify_gcc() {
local gcc="$1"
if [ -z "$gcc" ]; then
return 1
fi
local version="$("$gcc" --version || true)"
if [ -z "$version" ]; then
return 1
fi
if echo "$version" | grep LLVM >/dev/null; then
return 1
fi
echo "$gcc"
}
version() {
echo "ruby-build ${RUBY_BUILD_VERSION}"
}
usage() {
{ version
echo "usage: ruby-build [-k|--keep] [-v|--verbose] definition prefix"
echo " ruby-build --definitions"
} >&2
if [ -z "$1" ]; then
exit 1
fi
}
list_definitions() {
{ for definition in "${RUBY_BUILD_ROOT}/share/ruby-build/"*; do
echo "${definition##*/}"
done
} | sort
}
unset VERBOSE
unset KEEP_BUILD_PATH
RUBY_BUILD_ROOT="$(abs_dirname "$0")/.."
parse_options "$@"
for option in "${OPTIONS[@]}"; do
case "$option" in
"h" | "help" )
usage without_exiting
{ echo
echo " -k/--keep Do not remove source tree after installation"
echo " -v/--verbose Verbose mode: print compilation status to stdout"
echo " --definitions List all built-in definitions"
echo
} >&2
exit 0
;;
"definitions" )
list_definitions
exit 0
;;
"k" | "keep" )
KEEP_BUILD_PATH=true
;;
"v" | "verbose" )
VERBOSE=true
;;
"version" )
version
exit 0
;;
esac
done
DEFINITION_PATH="${ARGUMENTS[0]}"
if [ -z "$DEFINITION_PATH" ]; then
usage
elif [ ! -e "$DEFINITION_PATH" ]; then
BUILTIN_DEFINITION_PATH="${RUBY_BUILD_ROOT}/share/ruby-build/${DEFINITION_PATH}"
if [ -e "$BUILTIN_DEFINITION_PATH" ]; then
DEFINITION_PATH="$BUILTIN_DEFINITION_PATH"
else
echo "ruby-build: definition not found: ${DEFINITION_PATH}" >&2
exit 1
fi
fi
PREFIX_PATH="${ARGUMENTS[1]}"
if [ -z "$PREFIX_PATH" ]; then
usage
fi
if [ -z "$TMPDIR" ]; then
TMP="/tmp"
else
TMP="${TMPDIR%/}"
fi
if [ -n "$RUBY_BUILD_CACHE_PATH" ] && [ -d "$RUBY_BUILD_CACHE_PATH" ]; then
RUBY_BUILD_CACHE_PATH="${RUBY_BUILD_CACHE_PATH%/}"
else
unset RUBY_BUILD_CACHE_PATH
fi
SEED="$(date "+%Y%m%d%H%M%S").$$"
LOG_PATH="${TMP}/ruby-build.${SEED}.log"
RUBY_BIN="${PREFIX_PATH}/bin/ruby"
CWD="$(pwd)"
if [ -z "$RUBY_BUILD_BUILD_PATH" ]; then
BUILD_PATH="${TMP}/ruby-build.${SEED}"
else
BUILD_PATH="$RUBY_BUILD_BUILD_PATH"
fi
exec 4<> "$LOG_PATH" # open the log file at fd 4
if [ -n "$VERBOSE" ]; then
tail -f "$LOG_PATH" &
trap "kill 0" SIGINT SIGTERM EXIT
fi
export LDFLAGS="-L'${PREFIX_PATH}/lib' ${LDFLAGS}"
export CPPFLAGS="-I'${PREFIX_PATH}/include' ${CPPFLAGS}"
unset RUBYOPT
unset RUBYLIB
trap build_failed ERR
mkdir -p "$BUILD_PATH"
source "$DEFINITION_PATH"
[ -z "${KEEP_BUILD_PATH}" ] && rm -fr "$BUILD_PATH"
trap - ERR