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.

159 rivejä
4.3 KiB

  1. #!/usr/bin/env bash
  2. # Summary: Rehash pyenv shims (run this after installing executables)
  3. set -e
  4. [ -n "$PYENV_DEBUG" ] && set -x
  5. SHIM_PATH="${PYENV_ROOT}/shims"
  6. PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim"
  7. # Create the shims directory if it doesn't already exist.
  8. mkdir -p "$SHIM_PATH"
  9. # Ensure only one instance of pyenv-rehash is running at a time by
  10. # setting the shell's `noclobber` option and attempting to write to
  11. # the prototype shim file. If the file already exists, print a warning
  12. # to stderr and exit with a non-zero status.
  13. set -o noclobber
  14. { echo > "$PROTOTYPE_SHIM_PATH"
  15. } 2>/dev/null ||
  16. { if [ -w "$SHIM_PATH" ]; then
  17. echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists"
  18. else
  19. echo "pyenv: cannot rehash: $SHIM_PATH isn't writable"
  20. fi
  21. exit 1
  22. } >&2
  23. set +o noclobber
  24. # If we were able to obtain a lock, register a trap to clean up the
  25. # prototype shim when the process exits.
  26. trap remove_prototype_shim EXIT
  27. remove_prototype_shim() {
  28. rm -f "$PROTOTYPE_SHIM_PATH"
  29. }
  30. # The prototype shim file is a script that re-execs itself, passing
  31. # its filename and any arguments to `pyenv exec`. This file is
  32. # hard-linked for every executable and then removed. The linking
  33. # technique is fast, uses less disk space than unique files, and also
  34. # serves as a locking mechanism.
  35. create_prototype_shim() {
  36. cat > "$PROTOTYPE_SHIM_PATH" <<SH
  37. #!/usr/bin/env bash
  38. set -e
  39. [ -n "\$PYENV_DEBUG" ] && set -x
  40. program="\${0##*/}"
  41. if [ "\$program" = "python" ]; then
  42. for arg; do
  43. case "\$arg" in
  44. -c* | -- ) break ;;
  45. */* )
  46. if [ -f "\$arg" ]; then
  47. export PYENV_DIR="\${arg%/*}"
  48. break
  49. fi
  50. ;;
  51. esac
  52. done
  53. fi
  54. export PYENV_ROOT="$PYENV_ROOT"
  55. exec "$(command -v pyenv)" exec "\$program" "\$@"
  56. SH
  57. chmod +x "$PROTOTYPE_SHIM_PATH"
  58. }
  59. # If the contents of the prototype shim file differ from the contents
  60. # of the first shim in the shims directory, assume pyenv has been
  61. # upgraded and the existing shims need to be removed.
  62. remove_outdated_shims() {
  63. for shim in *; do
  64. if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
  65. for shim in *; do rm -f "$shim"; done
  66. fi
  67. break
  68. done
  69. }
  70. # The basename of each argument passed to `make_shims` will be
  71. # registered for installation as a shim. In this way, plugins may call
  72. # `make_shims` with a glob to register many shims at once.
  73. make_shims() {
  74. local shims=("$@")
  75. for file in "${shims[@]}"; do
  76. local shim="${file##*/}"
  77. register_shim "$shim"
  78. done
  79. }
  80. # Create an empty array for the list of registered shims and an empty
  81. # string to use as a search index.
  82. registered_shims=()
  83. registered_shims_index=""
  84. # We will keep track of shims registered for installation with the
  85. # global `registered_shims` array and with a global search index
  86. # string. The array will let us iterate over all registered shims. The
  87. # index string will let us quickly check whether a shim with the given
  88. # name has been registered or not.
  89. register_shim() {
  90. local shim="$@"
  91. registered_shims["${#registered_shims[@]}"]="$shim"
  92. registered_shims_index="$registered_shims_index/$shim/"
  93. }
  94. # To install all the registered shims, we iterate over the
  95. # `registered_shims` array and create a link if one does not already
  96. # exist.
  97. install_registered_shims() {
  98. local shim
  99. for shim in "${registered_shims[@]}"; do
  100. [ -e "$shim" ] || ln -f "$PROTOTYPE_SHIM_PATH" "$shim"
  101. done
  102. }
  103. # Once the registered shims have been installed, we make a second pass
  104. # over the contents of the shims directory. Any file that is present
  105. # in the directory but has not been registered as a shim should be
  106. # removed.
  107. remove_stale_shims() {
  108. local shim
  109. for shim in *; do
  110. if [[ "$registered_shims_index" != *"/$shim/"* ]]; then
  111. rm -f "$shim"
  112. fi
  113. done
  114. }
  115. # Change to the shims directory.
  116. cd "$SHIM_PATH"
  117. shopt -s nullglob
  118. # Create the prototype shim, then register shims for all known
  119. # executables.
  120. create_prototype_shim
  121. remove_outdated_shims
  122. make_shims ../versions/*/bin/*
  123. # Restore the previous working directory.
  124. cd "$OLDPWD"
  125. # Allow plugins to register shims.
  126. OLDIFS="$IFS"
  127. IFS=$'\n' scripts=(`pyenv-hooks rehash`)
  128. IFS="$OLDIFS"
  129. for script in "${scripts[@]}"; do
  130. source "$script"
  131. done
  132. # Change back to the shims directory to install the registered shims
  133. # and remove stale shims.
  134. cd "$SHIM_PATH"
  135. install_registered_shims
  136. remove_stale_shims