Commit e71a4607 authored by Sam Stephenson's avatar Sam Stephenson
Browse files

Merge pull request #240 from sstephenson/mirrors

Faster package downloads via S3
parents 093e3903 57fb50cb
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -123,11 +123,56 @@ process.
* `RUBY_BUILD_BUILD_PATH` sets the location in which sources are
  downloaded and built. By default, this is a subdirectory of
  `TMPDIR`.
* `RUBY_BUILD_CACHE_PATH`, if set, specifies a directory to use for
  caching downloaded package files.
* `RUBY_BUILD_MIRROR_URL` overrides the default mirror URL root to one
  of your choosing.
* `RUBY_BUILD_SKIP_MIRROR`, if set, forces ruby-build to download
  packages from their original source URLs instead of using a mirror.
* `CC` sets the path to the C compiler.
* `CONFIGURE_OPTS` lets you pass additional options to `./configure`.
* `MAKE_OPTS` (or `MAKEOPTS`) lets you pass additional options to
  `make`.

### Checksum verification

If you have the `md5`, `openssl`, or `md5sum` tool installed,
ruby-build will automatically verify the MD5 checksum of each
downloaded package before installing it.

Checksums are optional and specified as anchors on the package URL in
each definition. (All bundled definitions include checksums.)

### Package download mirrors

ruby-build will first attempt to download package files from a mirror
hosted on Amazon S3. If a package is not available on the mirror, if
the mirror is down, or if the download is corrupt, ruby-build will
fall back to the official URL specified in the defintion file.

You can point ruby-build to another mirror by specifying the
`RUBY_BUILD_MIRROR_URL` environment variable--useful if you'd like to
run your own local mirror, for example. Package mirror URLs are
constructed by joining this variable with the MD5 checksum of the
package file.

If you don't have an MD5 program installed, ruby-build will skip the
download mirror and use official URLs instead. You can force
ruby-build to bypass the mirror by setting the
`RUBY_BUILD_SKIP_MIRROR` environment variable.

### Package download caching

You can instruct ruby-build to keep a local cache of downloaded
package files by setting the `RUBY_BUILD_CACHE_PATH` environment
variable. When set, package files will be kept in this directory after
the first successful download and reused by subsequent invocations of
`ruby-build` and `rbenv install`.

The `rbenv install` command defaults this path to `~/.rbenv/cache`, so
in most cases you can enable download caching simply by creating that
directory.

### Keeping the build directory after installation

Both `ruby-build` and `rbenv install` accept the `-k` or `--keep`
+134 −14
Original line number Diff line number Diff line
@@ -112,7 +112,9 @@ install_package_using() {
  make_package "$package_name" $*
  popd >&4

  echo "Installed ${package_name} to ${PREFIX_PATH}" >&2
  { echo "Installed ${package_name} to ${PREFIX_PATH}"
    echo
  } >&2
}

make_package() {
@@ -127,34 +129,135 @@ make_package() {
  popd >&4
}

fetch_url() {
compute_md5() {
  if type md5 &>/dev/null; then
    md5 -q
  elif type openssl &>/dev/null; then
    openssl md5
  elif type md5sum &>/dev/null; then
    local output="$(md5sum -b)"
    echo "${output% *}"
  else
    return 1
  fi
}

verify_checksum() {
  # If there's no MD5 support, return success
  [ -n "$HAS_MD5_SUPPORT" ] || return 0

  # If the specified filename doesn't exist, return failure
  local filename="$1"
  [ -e "$filename" ] || return 1

  # If there's no expected checksum, return success
  local expected_checksum="$2"
  [ -n "$expected_checksum" ] || return 0

  # If the computed checksum is empty, return failure
  local computed_checksum="$(compute_md5 < "$filename")"
  [ -n "$computed_checksum" ] || return 1

  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
}

http() {
  local method="$1"
  local url="$2"
  [ -n "$url" ] || return 1

  if type curl &>/dev/null; then
    curl "$@"
    "http_${method}_curl" "$url"
  elif type wget &>/dev/null; then
    wget -O- "$@"
    "http_${method}_wget" "$url"
  else
    echo "error: please install \`curl\` or \`wget\` and try again" >&2
    exit 1
  fi
}

http_head_curl() {
  curl -sILf "$1" >&4 2>&1
}

http_get_curl() {
  curl -sSLf "$1"
}

http_head_wget() {
  wget -q --spider "$1" >&4 2>&1
}

http_get_wget() {
  wget -nv -O- "$1"
}

fetch_tarball() {
  local package_name="$1"
  local package_url="$2"
  local filename="${package_name}.tar.gz"
  local mirror_url

  if [ -n "$RUBY_BUILD_CACHE_PATH" ]; then
    filename="${RUBY_BUILD_CACHE_PATH}/${filename}"
  local checksum="${package_url#*\#}"
  if [ -n "$checksum" ]; then
    package_url="${package_url%%#*}"

    if [ -n "$RUBY_BUILD_MIRROR_URL" ]; then
      mirror_url="${RUBY_BUILD_MIRROR_URL}/$checksum"
    fi
  fi

  if [ ! -e "$filename" ]; then
    echo "Downloading ${package_url}..." >&2
    { fetch_url "$package_url" > "${package_name}.tar.gz"
      [ -z "$RUBY_BUILD_CACHE_PATH" ] || mv "${package_name}.tar.gz" "$filename"
  local package_filename="${package_name}.tar.gz"
  symlink_tarball_from_cache "$package_filename" "$checksum" || {
    echo "Downloading ${package_filename}..." >&2
    { http head "$mirror_url" &&
      download_tarball "$mirror_url" "$package_filename" "$checksum"
    } ||
    download_tarball "$package_url" "$package_filename" "$checksum"
  }

  { tar xzvf "$package_filename"
    rm -f "$package_filename"
  } >&4 2>&1
  fi
}

symlink_tarball_from_cache() {
  [ -n "$RUBY_BUILD_CACHE_PATH" ] || return 1

  local package_filename="$1"
  local cached_package_filename="${RUBY_BUILD_CACHE_PATH}/$package_filename"
  local checksum="$2"

  { verify_checksum "$cached_package_filename" "$checksum"
    ln -s "$cached_package_filename" "$package_filename"
  } >&4 2>&1 || return 1
}

download_tarball() {
  local package_url="$1"
  [ -n "$package_url" ] || return 1

  tar xzvf "$filename" >&4 2>&1
  local package_filename="$2"
  local checksum="$3"

  echo "-> $package_url" >&2

  { http get "$package_url" > "$package_filename"
    verify_checksum "$package_filename" "$checksum"
  } >&4 2>&1 || return 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 || return 1
  fi
}

fetch_git() {
@@ -477,6 +580,23 @@ else
  unset RUBY_BUILD_CACHE_PATH
fi

if [ -z "$RUBY_BUILD_MIRROR_URL" ]; then
  RUBY_BUILD_MIRROR_URL="http://cloud.github.com/downloads/sstephenson/ruby-build-download-mirror"
else
  RUBY_BUILD_MIRROR_URL="${RUBY_BUILD_MIRROR_URL%/}"
fi

if [ -n "$RUBY_BUILD_SKIP_MIRROR" ]; then
  unset RUBY_BUILD_MIRROR_URL
fi

if echo test | compute_md5 >/dev/null; then
  HAS_MD5_SUPPORT=1
else
  unset HAS_MD5_SUPPORT
  unset RUBY_BUILD_MIRROR_URL
fi

SEED="$(date "+%Y%m%d%H%M%S").$$"
LOG_PATH="${TMP}/ruby-build.${SEED}.log"
RUBY_BIN="${PREFIX_PATH}/bin/ruby"
+2 −2
Original line number Diff line number Diff line
require_gcc
install_package "ruby-1.8.6-p383" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p383.tar.gz"
install_package "rubygems-1.3.7" "http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz" ruby
install_package "ruby-1.8.6-p383" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p383.tar.gz#4f49544d4a4d0d34e9d86c41e853db2e"
install_package "rubygems-1.3.7" "http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz#e85cfadd025ff6ab689375adbf344bbe" ruby
+2 −2
Original line number Diff line number Diff line
require_gcc
install_package "ruby-1.8.6-p420" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p420.tar.gz"
install_package "rubygems-1.3.7" "http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz" ruby
install_package "ruby-1.8.6-p420" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p420.tar.gz#ca1eee44f842e93b5098bc5a2bb9a40b"
install_package "rubygems-1.3.7" "http://production.cf.rubygems.org/rubygems/rubygems-1.3.7.tgz#e85cfadd025ff6ab689375adbf344bbe" ruby
+2 −2
Original line number Diff line number Diff line
require_gcc
install_package "ruby-1.8.7-p249" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p249.tar.gz"
install_package "rubygems-1.6.2" "http://production.cf.rubygems.org/rubygems/rubygems-1.6.2.tgz" ruby
install_package "ruby-1.8.7-p249" "http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p249.tar.gz#d7db7763cffad279952eb7e9bbfc221c"
install_package "rubygems-1.6.2" "http://production.cf.rubygems.org/rubygems/rubygems-1.6.2.tgz#0c95a9869914ba1a45bf71d3b8048420" ruby
Loading