#!/usr/bin/env bash RUBY_BUILD_VERSION="20120524" set -E exec 3<&2 # preserve original stderr at fd 3 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_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 } fetch_url() { if type curl &>/dev/null; then curl "$@" 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" echo "Downloading ${package_url}..." >&2 { fetch_url "$package_url" > "${package_name}.tar.gz" tar xzvf "${package_name}.tar.gz" } >&4 2>&1 } 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 } 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 [-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 RUBY_BUILD_ROOT="$(abs_dirname "$0")/.." case "$1" in "-h" | "--help" ) usage without_exiting { echo 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 ;; "--version" ) version exit 0 ;; "-v" | "--verbose" ) VERBOSE=true shift ;; esac DEFINITION_PATH="$1" 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="$2" if [ -z "$PREFIX_PATH" ]; then usage fi OPTIONS="$3" for option in $OPTIONS; do case "$option" in "-k" | "--keep" ) KEEP_BUILD_PATH="y" ;; esac done if [ -z "$TMPDIR" ]; then TMP="/tmp" else TMP="${TMPDIR%/}" 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