at 24.11-pre 148 lines 5.1 kB view raw
1# This setup hook causes the fixup phase to rewrite all script 2# interpreter file names (`#! /path') to paths found in $PATH. E.g., 3# /bin/sh will be rewritten to /nix/store/<hash>-some-bash/bin/sh. 4# /usr/bin/env gets special treatment so that ".../bin/env python" is 5# rewritten to /nix/store/<hash>/bin/python. Interpreters that are 6# already in the store are left untouched. 7# A script file must be marked as executable, otherwise it will not be 8# considered. 9 10fixupOutputHooks+=(patchShebangsAuto) 11 12# Run patch shebangs on a directory or file. 13# Can take multiple paths as arguments. 14# patchShebangs [--build | --host | --update] [--] PATH... 15 16# Flags: 17# --build : Lookup commands available at build-time 18# --host : Lookup commands available at runtime 19# --update : Update shebang paths that are in Nix store 20 21# Example use cases, 22# $ patchShebangs --host /nix/store/...-hello-1.0/bin 23# $ patchShebangs --build configure 24 25patchShebangs() { 26 local pathName 27 local update 28 29 while [[ $# -gt 0 ]]; do 30 case "$1" in 31 --host) 32 pathName=HOST_PATH 33 shift 34 ;; 35 --build) 36 pathName=PATH 37 shift 38 ;; 39 --update) 40 update=true 41 shift 42 ;; 43 --) 44 shift 45 break 46 ;; 47 -*|--*) 48 echo "Unknown option $1 supplied to patchShebangs" >&2 49 return 1 50 ;; 51 *) 52 break 53 ;; 54 esac 55 done 56 57 echo "patching script interpreter paths in $@" 58 local f 59 local oldPath 60 local newPath 61 local arg0 62 local args 63 local oldInterpreterLine 64 local newInterpreterLine 65 66 if [[ $# -eq 0 ]]; then 67 echo "No arguments supplied to patchShebangs" >&2 68 return 0 69 fi 70 71 local f 72 while IFS= read -r -d $'\0' f; do 73 isScript "$f" || continue 74 75 # read exits unclean if the shebang does not end with a newline, but still assigns the variable. 76 # So if read returns errno != 0, we check if the assigned variable is non-empty and continue. 77 read -r oldInterpreterLine < "$f" || [ "$oldInterpreterLine" ] 78 79 read -r oldPath arg0 args <<< "${oldInterpreterLine:2}" 80 81 if [[ -z "${pathName:-}" ]]; then 82 if [[ -n $strictDeps && $f == "$NIX_STORE"* ]]; then 83 pathName=HOST_PATH 84 else 85 pathName=PATH 86 fi 87 fi 88 89 if [[ "$oldPath" == *"/bin/env" ]]; then 90 if [[ $arg0 == "-S" ]]; then 91 arg0=${args%% *} 92 args=${args#* } 93 newPath="$(PATH="${!pathName}" command -v "env" || true)" 94 args="-S $(PATH="${!pathName}" command -v "$arg0" || true) $args" 95 96 # Check for unsupported 'env' functionality: 97 # - options: something starting with a '-' besides '-S' 98 # - environment variables: foo=bar 99 elif [[ $arg0 == "-"* || $arg0 == *"="* ]]; then 100 echo "$f: unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)" >&2 101 exit 1 102 else 103 newPath="$(PATH="${!pathName}" command -v "$arg0" || true)" 104 fi 105 else 106 if [[ -z $oldPath ]]; then 107 # If no interpreter is specified linux will use /bin/sh. Set 108 # oldpath="/bin/sh" so that we get /nix/store/.../sh. 109 oldPath="/bin/sh" 110 fi 111 112 newPath="$(PATH="${!pathName}" command -v "$(basename "$oldPath")" || true)" 113 114 args="$arg0 $args" 115 fi 116 117 # Strip trailing whitespace introduced when no arguments are present 118 newInterpreterLine="$newPath $args" 119 newInterpreterLine=${newInterpreterLine%${newInterpreterLine##*[![:space:]]}} 120 121 if [[ -n "$oldPath" && ( "$update" == true || "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ) ]]; then 122 if [[ -n "$newPath" && "$newPath" != "$oldPath" ]]; then 123 echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\"" 124 # escape the escape chars so that sed doesn't interpret them 125 escapedInterpreterLine=${newInterpreterLine//\\/\\\\} 126 127 # Preserve times, see: https://github.com/NixOS/nixpkgs/pull/33281 128 timestamp=$(stat --printf "%y" "$f") 129 sed -i -e "1 s|.*|#\!$escapedInterpreterLine|" "$f" 130 touch --date "$timestamp" "$f" 131 fi 132 fi 133 done < <(find "$@" -type f -perm -0100 -print0) 134} 135 136patchShebangsAuto () { 137 if [[ -z "${dontPatchShebangs-}" && -e "$prefix" ]]; then 138 139 # Dev output will end up being run on the build platform. An 140 # example case of this is sdl2-config. Otherwise, we can just 141 # use the runtime path (--host). 142 if [[ "$output" != out && "$output" = "$outputDev" ]]; then 143 patchShebangs --build "$prefix" 144 else 145 patchShebangs --host "$prefix" 146 fi 147 fi 148}