#!/usr/bin/env bash # Summary: Rehash rbenv shims (run this after installing binaries) # Usage: rbenv rehash set -e [ -n "$RBENV_DEBUG" ] && set -x SHIM_PATH="${RBENV_ROOT}/shims" PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.rbenv-shim" # Create the shims directory if it doesn't already exist. mkdir -p "$SHIM_PATH" # Ensure only one instance of rbenv-rehash is running at a time by # setting the shell's `noclobber` option and attempting to write to # the prototype shim file. If the file already exists, print a warning # to stderr and exit with a non-zero status. set -o noclobber { echo > "$PROTOTYPE_SHIM_PATH" } 2>/dev/null || { echo "rbenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists" exit 1 } >&2 set +o noclobber # If we were able to obtain a lock, register a trap to clean up the # prototype shim when the process exits. trap remove_prototype_shim EXIT remove_prototype_shim() { rm -f "$PROTOTYPE_SHIM_PATH" } # The prototype shim file is a script that re-execs itself, passing # its filename and any arguments to `rbenv exec`. This file is # hard-linked for every binary and then removed. The linking technique # is fast, uses less disk space than unique files, and also serves as # a locking mechanism. create_prototype_shim() { cat > "$PROTOTYPE_SHIM_PATH" </dev/null 2>&1; then for shim in *; do rm -f "$shim"; done fi break done } # The basename of each argument passed to `make_shims` will be # registered for installation as a shim. In this way, plugins may call # `make_shims` with a glob to register many shims at once. make_shims() { local shims="$@" for file in $shims; do local shim="${file##*/}" register_shim "$shim" done } # Create an empty array for the list of registered shims and an empty # string to use as a search index. registered_shims=() registered_shims_index="" # We will keep track of shims registered for installation with the # global `reigstered_shims` array and with a global search index # string. The array will let us iterate over all registered shims. The # index string will let us quickly check whether a shim with the given # name has been registered or not. register_shim() { local shim="$@" registered_shims["${#registered_shims[@]}"]="$shim" registered_shims_index="$registered_shims_index/$shim/" } # To install all the registered shims, we iterate over the # `registered_shims` array and create a link if one does not already # exist. install_registered_shims() { local shim for shim in "${registered_shims[@]}"; do [ -e "$shim" ] || ln -f "$PROTOTYPE_SHIM_PATH" "$shim" done } # Once the registered shims have been installed, we make a second pass # over the contents of the shims directory. Any file that is present # in the directory but has not been registered as a shim should be # removed. remove_stale_shims() { local shim for shim in *; do if [[ "$registered_shims_index" != *"/$shim/"* ]]; then rm -f "$shim" fi done } # Change to the shims directory. cd "$SHIM_PATH" shopt -s nullglob # Create the prototype shim, then register shims for all known binaries. create_prototype_shim remove_outdated_shims make_shims ../versions/*/bin/* # Restore the previous working directory. cd "$OLDPWD" # Allow plugins to register shims. for script in $(rbenv-hooks rehash); do source "$script" done # Change back to the shims directory to install the registered shims # and remove stale shims. cd "$SHIM_PATH" install_registered_shims remove_stale_shims