2012-08-31 06:23:41 +00:00
|
|
|
#!/usr/bin/env bash
|
2013-01-18 08:41:41 +00:00
|
|
|
# Summary: Rehash pyenv shims (run this after installing executables)
|
|
|
|
|
2012-08-31 06:23:41 +00:00
|
|
|
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"
|
|
|
|
|
2018-04-19 01:02:16 +00:00
|
|
|
acquire_lock() {
|
|
|
|
# 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.
|
|
|
|
local ret
|
|
|
|
set -o noclobber
|
|
|
|
echo > "$PROTOTYPE_SHIM_PATH" 2>| /dev/null || ret=1
|
|
|
|
set +o noclobber
|
|
|
|
[ -z "${ret}" ]
|
|
|
|
}
|
2012-08-31 06:23:41 +00:00
|
|
|
|
|
|
|
# If we were able to obtain a lock, register a trap to clean up the
|
|
|
|
# prototype shim when the process exits.
|
2018-04-19 01:02:16 +00:00
|
|
|
trap release_lock EXIT
|
2012-08-31 06:23:41 +00:00
|
|
|
|
|
|
|
remove_prototype_shim() {
|
|
|
|
rm -f "$PROTOTYPE_SHIM_PATH"
|
|
|
|
}
|
|
|
|
|
2018-04-19 01:02:16 +00:00
|
|
|
release_lock() {
|
|
|
|
remove_prototype_shim
|
|
|
|
}
|
|
|
|
|
2018-04-24 00:37:29 +00:00
|
|
|
if [ ! -w "$SHIM_PATH" ]; then
|
|
|
|
echo "pyenv: cannot rehash: $SHIM_PATH isn't writable"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
2018-04-19 01:02:16 +00:00
|
|
|
unset acquired
|
2018-06-01 06:11:01 +00:00
|
|
|
for (( i=1; i<="${PYENV_REHASH_TIMEOUT:-60}"; i++ )); do
|
2018-04-19 01:02:16 +00:00
|
|
|
if acquire_lock 2>/dev/null; then
|
|
|
|
acquired=1
|
|
|
|
break
|
|
|
|
else
|
|
|
|
# POSIX sleep(1) doesn't provides time precision of subsecond
|
|
|
|
sleep 1
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
|
|
if [ -z "${acquired}" ]; then
|
2018-04-24 00:37:29 +00:00
|
|
|
echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists"
|
2018-04-19 01:02:16 +00:00
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
2012-08-31 06:23:41 +00:00
|
|
|
# The prototype shim file is a script that re-execs itself, passing
|
|
|
|
# its filename and any arguments to `pyenv exec`. This file is
|
2013-01-18 09:57:08 +00:00
|
|
|
# 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.
|
2012-08-31 06:23:41 +00:00
|
|
|
create_prototype_shim() {
|
|
|
|
cat > "$PROTOTYPE_SHIM_PATH" <<SH
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
set -e
|
2013-01-18 09:57:08 +00:00
|
|
|
[ -n "\$PYENV_DEBUG" ] && set -x
|
|
|
|
|
|
|
|
program="\${0##*/}"
|
2015-05-18 14:34:02 +00:00
|
|
|
if [[ "\$program" = "python"* ]]; then
|
2013-01-18 09:57:08 +00:00
|
|
|
for arg; do
|
|
|
|
case "\$arg" in
|
|
|
|
-c* | -- ) break ;;
|
|
|
|
*/* )
|
|
|
|
if [ -f "\$arg" ]; then
|
2015-07-08 14:20:14 +00:00
|
|
|
export PYENV_FILE_ARG="\$arg"
|
2013-01-18 09:57:08 +00:00
|
|
|
break
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
|
2012-08-31 06:23:41 +00:00
|
|
|
export PYENV_ROOT="$PYENV_ROOT"
|
2013-01-18 09:57:08 +00:00
|
|
|
exec "$(command -v pyenv)" exec "\$program" "\$@"
|
2012-08-31 06:23:41 +00:00
|
|
|
SH
|
|
|
|
chmod +x "$PROTOTYPE_SHIM_PATH"
|
|
|
|
}
|
|
|
|
|
2013-01-18 09:57:08 +00:00
|
|
|
# If the contents of the prototype shim file differ from the contents
|
2013-02-06 07:05:29 +00:00
|
|
|
# of the first shim in the shims directory, assume pyenv has been
|
2013-01-18 09:57:08 +00:00
|
|
|
# upgraded and the existing shims need to be removed.
|
|
|
|
remove_outdated_shims() {
|
2014-11-30 15:20:53 +00:00
|
|
|
local shim
|
|
|
|
for shim in "$SHIM_PATH"/*; do
|
2013-01-18 09:57:08 +00:00
|
|
|
if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
|
2014-11-30 15:20:53 +00:00
|
|
|
rm -f "$SHIM_PATH"/*
|
2013-01-18 09:57:08 +00:00
|
|
|
fi
|
|
|
|
break
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2014-11-30 15:20:53 +00:00
|
|
|
# List basenames of executables for every Python version
|
|
|
|
list_executable_names() {
|
2015-10-27 09:49:29 +00:00
|
|
|
local version file
|
2015-11-21 04:13:52 +00:00
|
|
|
pyenv-versions --bare --skip-aliases | \
|
2017-06-23 22:12:09 +00:00
|
|
|
while read -r version; do
|
2015-11-21 04:13:52 +00:00
|
|
|
for file in "${PYENV_ROOT}/versions/${version}/bin/"*; do
|
2015-10-27 09:49:29 +00:00
|
|
|
echo "${file##*/}"
|
|
|
|
done
|
2014-11-30 15:20:53 +00:00
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2012-08-31 06:23:41 +00:00
|
|
|
# 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() {
|
2014-11-30 15:20:53 +00:00
|
|
|
local file shim
|
|
|
|
for file; do
|
|
|
|
shim="${file##*/}"
|
2012-08-31 06:23:41 +00:00
|
|
|
register_shim "$shim"
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2014-11-30 15:20:53 +00:00
|
|
|
registered_shims=" "
|
2012-08-31 06:23:41 +00:00
|
|
|
|
2014-11-30 15:20:53 +00:00
|
|
|
# Registers the name of a shim to be generated.
|
2012-08-31 06:23:41 +00:00
|
|
|
register_shim() {
|
2014-11-30 15:20:53 +00:00
|
|
|
registered_shims="${registered_shims}${1} "
|
2012-08-31 06:23:41 +00:00
|
|
|
}
|
|
|
|
|
2014-11-30 15:20:53 +00:00
|
|
|
# Install all the shims registered via `make_shims` or `register_shim` directly.
|
2012-08-31 06:23:41 +00:00
|
|
|
install_registered_shims() {
|
2014-11-30 15:20:53 +00:00
|
|
|
local shim file
|
|
|
|
for shim in $registered_shims; do
|
|
|
|
file="${SHIM_PATH}/${shim}"
|
2015-10-28 20:35:39 +00:00
|
|
|
[ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
|
2012-08-31 06:23:41 +00:00
|
|
|
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() {
|
2013-01-18 09:57:08 +00:00
|
|
|
local shim
|
2014-11-30 15:20:53 +00:00
|
|
|
for shim in "$SHIM_PATH"/*; do
|
|
|
|
if [[ "$registered_shims" != *" ${shim##*/} "* ]]; then
|
2013-01-18 09:57:08 +00:00
|
|
|
rm -f "$shim"
|
2012-08-31 06:23:41 +00:00
|
|
|
fi
|
|
|
|
done
|
|
|
|
}
|
|
|
|
|
2013-01-18 09:57:08 +00:00
|
|
|
shopt -s nullglob
|
2012-08-31 06:23:41 +00:00
|
|
|
|
2013-01-18 09:57:08 +00:00
|
|
|
# Create the prototype shim, then register shims for all known
|
|
|
|
# executables.
|
2012-08-31 06:23:41 +00:00
|
|
|
create_prototype_shim
|
2013-01-18 09:57:08 +00:00
|
|
|
remove_outdated_shims
|
2017-06-23 22:12:09 +00:00
|
|
|
# shellcheck disable=SC2046
|
2014-11-30 15:20:53 +00:00
|
|
|
make_shims $(list_executable_names | sort -u)
|
2012-08-31 06:23:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
# Allow plugins to register shims.
|
2014-01-02 13:26:22 +00:00
|
|
|
OLDIFS="$IFS"
|
|
|
|
IFS=$'\n' scripts=(`pyenv-hooks rehash`)
|
|
|
|
IFS="$OLDIFS"
|
|
|
|
|
|
|
|
for script in "${scripts[@]}"; do
|
2012-08-31 06:23:41 +00:00
|
|
|
source "$script"
|
|
|
|
done
|
|
|
|
|
|
|
|
install_registered_shims
|
|
|
|
remove_stale_shims
|