Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

203 Zeilen
4.9 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. acquire_lock() {
  10. # Ensure only one instance of pyenv-rehash is running at a time by
  11. # setting the shell's `noclobber` option and attempting to write to
  12. # the prototype shim file. If the file already exists, print a warning
  13. # to stderr and exit with a non-zero status.
  14. local ret
  15. set -o noclobber
  16. echo > "$PROTOTYPE_SHIM_PATH" 2>| /dev/null || ret=1
  17. set +o noclobber
  18. [ -z "${ret}" ]
  19. }
  20. # If we were able to obtain a lock, register a trap to clean up the
  21. # prototype shim when the process exits.
  22. trap release_lock EXIT
  23. remove_prototype_shim() {
  24. rm -f "$PROTOTYPE_SHIM_PATH"
  25. }
  26. release_lock() {
  27. remove_prototype_shim
  28. }
  29. if [ ! -w "$SHIM_PATH" ]; then
  30. echo "pyenv: cannot rehash: $SHIM_PATH isn't writable"
  31. exit 1
  32. fi
  33. unset acquired
  34. start=$SECONDS
  35. while (( SECONDS <= start + ${PYENV_REHASH_TIMEOUT:-60} )); do
  36. if acquire_lock 2>/dev/null; then
  37. acquired=1
  38. break
  39. else
  40. # POSIX sleep(1) doesn't provide subsecond precision, but many others do
  41. sleep 0.1 2>/dev/null || sleep 1
  42. fi
  43. done
  44. if [ -z "${acquired}" ]; then
  45. echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists"
  46. exit 1
  47. fi
  48. # The prototype shim file is a script that re-execs itself, passing
  49. # its filename and any arguments to `pyenv exec`. This file is
  50. # hard-linked for every executable and then removed. The linking
  51. # technique is fast, uses less disk space than unique files, and also
  52. # serves as a locking mechanism.
  53. create_prototype_shim() {
  54. cat > "$PROTOTYPE_SHIM_PATH" <<SH
  55. #!/usr/bin/env bash
  56. set -e
  57. [ -n "\$PYENV_DEBUG" ] && set -x
  58. program="\${0##*/}"
  59. if [[ "\$program" = "python"* ]]; then
  60. for arg; do
  61. case "\$arg" in
  62. -c* | -- ) break ;;
  63. */* )
  64. if [ -f "\$arg" ]; then
  65. export PYENV_FILE_ARG="\$arg"
  66. break
  67. fi
  68. ;;
  69. esac
  70. done
  71. fi
  72. export PYENV_ROOT="$PYENV_ROOT"
  73. exec "$(command -v pyenv)" exec "\$program" "\$@"
  74. SH
  75. chmod +x "$PROTOTYPE_SHIM_PATH"
  76. }
  77. # If the contents of the prototype shim file differ from the contents
  78. # of the first shim in the shims directory, assume pyenv has been
  79. # upgraded and the existing shims need to be removed.
  80. remove_outdated_shims() {
  81. local shim
  82. for shim in "$SHIM_PATH"/*; do
  83. if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
  84. rm -f "$SHIM_PATH"/*
  85. fi
  86. break
  87. done
  88. }
  89. # List basenames of executables for every Python version
  90. list_executable_names() {
  91. local version file
  92. pyenv-versions --bare --skip-aliases | \
  93. while read -r version; do
  94. for file in "${PYENV_ROOT}/versions/${version}/bin/"*; do
  95. echo "${file##*/}"
  96. done
  97. done
  98. }
  99. # The basename of each argument passed to `make_shims` will be
  100. # registered for installation as a shim. In this way, plugins may call
  101. # `make_shims` with a glob to register many shims at once.
  102. make_shims() {
  103. local file shim
  104. for file; do
  105. shim="${file##*/}"
  106. register_shim "$shim"
  107. done
  108. }
  109. if ((${BASH_VERSINFO[0]} > 3)); then
  110. declare -A registered_shims
  111. # Registers the name of a shim to be generated.
  112. register_shim() {
  113. registered_shims["$1"]=1
  114. }
  115. # Install all shims registered via `make_shims` or `register_shim` directly.
  116. install_registered_shims() {
  117. local shim file
  118. for shim in "${!registered_shims[@]}"; do
  119. file="${SHIM_PATH}/${shim}"
  120. [ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
  121. done
  122. }
  123. # Once the registered shims have been installed, we make a second pass
  124. # over the contents of the shims directory. Any file that is present
  125. # in the directory but has not been registered as a shim should be
  126. # removed.
  127. remove_stale_shims() {
  128. local shim
  129. for shim in "$SHIM_PATH"/*; do
  130. if [[ ! ${registered_shims["${shim##*/}"]} ]]; then
  131. rm -f "$shim"
  132. fi
  133. done
  134. }
  135. else # Same for bash < 4.
  136. registered_shims=" "
  137. register_shim() {
  138. registered_shims="${registered_shims}${1} "
  139. }
  140. install_registered_shims() {
  141. local shim file
  142. for shim in $registered_shims; do
  143. file="${SHIM_PATH}/${shim}"
  144. [ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
  145. done
  146. }
  147. remove_stale_shims() {
  148. local shim
  149. for shim in "$SHIM_PATH"/*; do
  150. if [[ "$registered_shims" != *" ${shim##*/} "* ]]; then
  151. rm -f "$shim"
  152. fi
  153. done
  154. }
  155. fi
  156. shopt -s nullglob
  157. # Create the prototype shim, then register shims for all known
  158. # executables.
  159. create_prototype_shim
  160. remove_outdated_shims
  161. # shellcheck disable=SC2046
  162. make_shims $(list_executable_names | sort -u)
  163. # Allow plugins to register shims.
  164. OLDIFS="$IFS"
  165. IFS=$'\n' scripts=(`pyenv-hooks rehash`)
  166. IFS="$OLDIFS"
  167. for script in "${scripts[@]}"; do
  168. source "$script"
  169. done
  170. install_registered_shims
  171. remove_stale_shims