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.

152 lines
3.8 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_FILE_ARG="\$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. local shim
  64. for shim in "$SHIM_PATH"/*; do
  65. if ! diff "$PROTOTYPE_SHIM_PATH" "$shim" >/dev/null 2>&1; then
  66. rm -f "$SHIM_PATH"/*
  67. fi
  68. break
  69. done
  70. }
  71. # List basenames of executables for every Python version
  72. list_executable_names() {
  73. local version file
  74. pyenv-versions --bare --skip-aliases | \
  75. while read version; do
  76. for file in "${PYENV_ROOT}/versions/${version}/bin/"*; do
  77. echo "${file##*/}"
  78. done
  79. done
  80. }
  81. # The basename of each argument passed to `make_shims` will be
  82. # registered for installation as a shim. In this way, plugins may call
  83. # `make_shims` with a glob to register many shims at once.
  84. make_shims() {
  85. local file shim
  86. for file; do
  87. shim="${file##*/}"
  88. register_shim "$shim"
  89. done
  90. }
  91. registered_shims=" "
  92. # Registers the name of a shim to be generated.
  93. register_shim() {
  94. registered_shims="${registered_shims}${1} "
  95. }
  96. # Install all the shims registered via `make_shims` or `register_shim` directly.
  97. install_registered_shims() {
  98. local shim file
  99. for shim in $registered_shims; do
  100. file="${SHIM_PATH}/${shim}"
  101. [ -e "$file" ] || cp "$PROTOTYPE_SHIM_PATH" "$file"
  102. done
  103. }
  104. # Once the registered shims have been installed, we make a second pass
  105. # over the contents of the shims directory. Any file that is present
  106. # in the directory but has not been registered as a shim should be
  107. # removed.
  108. remove_stale_shims() {
  109. local shim
  110. for shim in "$SHIM_PATH"/*; do
  111. if [[ "$registered_shims" != *" ${shim##*/} "* ]]; then
  112. rm -f "$shim"
  113. fi
  114. done
  115. }
  116. shopt -s nullglob
  117. # Create the prototype shim, then register shims for all known
  118. # executables.
  119. create_prototype_shim
  120. remove_outdated_shims
  121. make_shims $(list_executable_names | sort -u)
  122. # Allow plugins to register shims.
  123. OLDIFS="$IFS"
  124. IFS=$'\n' scripts=(`pyenv-hooks rehash`)
  125. IFS="$OLDIFS"
  126. for script in "${scripts[@]}"; do
  127. source "$script"
  128. done
  129. install_registered_shims
  130. remove_stale_shims