mirror of https://github.com/jenv/jenv.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
4.7 KiB
187 lines
4.7 KiB
#!/usr/bin/env bash |
|
# Summary: Rehash jenv shims (run this after installing executables) |
|
|
|
|
|
set -e |
|
[ -n "$JENV_DEBUG" ] && set -x |
|
|
|
SHIM_PATH="${JENV_ROOT}/shims" |
|
PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.jenv-shim" |
|
|
|
# Create the shims directory if it doesn't already exist. |
|
mkdir -p "$SHIM_PATH" |
|
|
|
# Ensure only one instance of jenv-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 "jenv: 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" |
|
} |
|
|
|
remove_from_path() { |
|
local path_to_remove="$(expand_path "$1")" |
|
local result="" |
|
|
|
if [ -z "$path_to_remove" ]; then |
|
echo "${PATH}" |
|
return |
|
fi |
|
|
|
local paths |
|
IFS=: paths=($PATH) |
|
|
|
for path in "${paths[@]}"; do |
|
path="$(expand_path "$path" || true)" |
|
if [ -n "$path" ] && [ "$path" != "$path_to_remove" ]; then |
|
result="${result}${path}:" |
|
fi |
|
done |
|
|
|
echo "${result%:}" |
|
} |
|
|
|
expand_path() { |
|
if [ ! -d "$1" ]; then |
|
return 1 |
|
fi |
|
|
|
local cwd="$(pwd)" |
|
cd "$1" |
|
pwd |
|
cd "$cwd" |
|
} |
|
|
|
|
|
|
|
# The prototype shim file is a script that re-execs itself, passing |
|
# its filename and any arguments to `jenv 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 "\$JENV_DEBUG" ] && set -x |
|
|
|
program="\${0##*/}" |
|
if [ "\$program" = "ruby" ]; then |
|
for arg; do |
|
case "\$arg" in |
|
-e* | -- ) break ;; |
|
*/* ) |
|
if [ -f "\$arg" ]; then |
|
export JENV_DIR="\${arg%/*}" |
|
break |
|
fi |
|
;; |
|
esac |
|
done |
|
fi |
|
|
|
export JENV_ROOT="$JENV_ROOT" |
|
exec "$(command -v jenv)" 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 jenv 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 `registered_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 $(jenv-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
|
|
|