pyenv/libexec/pyenv-rehash
2013-01-18 19:10:35 +09:00

151 lines
4.1 KiB
Bash
Executable file

#!/usr/bin/env bash
# Summary: Rehash pyenv shims (run this after installing executables)
set -e
[ -n "$PYENV_DEBUG" ] && set -x
SHIM_PATH="${PYENV_ROOT}/shims"
PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim"
# Create the shims directory if it doesn't already exist.
mkdir -p "$SHIM_PATH"
# Ensure only one instance of pyenv-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 "pyenv: 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 `pyenv exec`. This file is
# hard-linked for every executable 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" <<SH
#!/usr/bin/env bash
set -e
[ -n "\$PYENV_DEBUG" ] && set -x
program="\${0##*/}"
if [ "\$program" = "python" ]; then
for arg; do
case "\$arg" in
-c* | -- ) break ;;
*/* )
if [ -f "\$arg" ]; then
export PYENV_DIR="\${arg%/*}"
break
fi
;;
esac
done
fi
export PYENV_ROOT="$PYENV_ROOT"
exec "$(command -v pyenv)" exec "\$program" "\$@"
SH
chmod +x "$PROTOTYPE_SHIM_PATH"
}
# If the contents of the prototype shim file differ from the contents
# of the first shim in the shims directory, assume rbenv has been
# upgraded and the existing shims need to be removed.
remove_outdated_shims() {
for shim in *; do
if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/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
# executables.
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 $(pyenv-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