lol
at 23.11-beta 145 lines 4.9 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 -r oldInterpreterLine < "$f" 76 read -r oldPath arg0 args <<< "${oldInterpreterLine:2}" 77 78 if [[ -z "${pathName:-}" ]]; then 79 if [[ -n $strictDeps && $f == "$NIX_STORE"* ]]; then 80 pathName=HOST_PATH 81 else 82 pathName=PATH 83 fi 84 fi 85 86 if [[ "$oldPath" == *"/bin/env" ]]; then 87 if [[ $arg0 == "-S" ]]; then 88 arg0=${args%% *} 89 args=${args#* } 90 newPath="$(PATH="${!pathName}" command -v "env" || true)" 91 args="-S $(PATH="${!pathName}" command -v "$arg0" || true) $args" 92 93 # Check for unsupported 'env' functionality: 94 # - options: something starting with a '-' besides '-S' 95 # - environment variables: foo=bar 96 elif [[ $arg0 == "-"* || $arg0 == *"="* ]]; then 97 echo "$f: unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)" >&2 98 exit 1 99 else 100 newPath="$(PATH="${!pathName}" command -v "$arg0" || true)" 101 fi 102 else 103 if [[ -z $oldPath ]]; then 104 # If no interpreter is specified linux will use /bin/sh. Set 105 # oldpath="/bin/sh" so that we get /nix/store/.../sh. 106 oldPath="/bin/sh" 107 fi 108 109 newPath="$(PATH="${!pathName}" command -v "$(basename "$oldPath")" || true)" 110 111 args="$arg0 $args" 112 fi 113 114 # Strip trailing whitespace introduced when no arguments are present 115 newInterpreterLine="$newPath $args" 116 newInterpreterLine=${newInterpreterLine%${newInterpreterLine##*[![:space:]]}} 117 118 if [[ -n "$oldPath" && ( "$update" == true || "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ) ]]; then 119 if [[ -n "$newPath" && "$newPath" != "$oldPath" ]]; then 120 echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\"" 121 # escape the escape chars so that sed doesn't interpret them 122 escapedInterpreterLine=${newInterpreterLine//\\/\\\\} 123 124 # Preserve times, see: https://github.com/NixOS/nixpkgs/pull/33281 125 timestamp=$(stat --printf "%y" "$f") 126 sed -i -e "1 s|.*|#\!$escapedInterpreterLine|" "$f" 127 touch --date "$timestamp" "$f" 128 fi 129 fi 130 done < <(find "$@" -type f -perm -0100 -print0) 131} 132 133patchShebangsAuto () { 134 if [[ -z "${dontPatchShebangs-}" && -e "$prefix" ]]; then 135 136 # Dev output will end up being run on the build platform. An 137 # example case of this is sdl2-config. Otherwise, we can just 138 # use the runtime path (--host). 139 if [[ "$output" != out && "$output" = "$outputDev" ]]; then 140 patchShebangs --build "$prefix" 141 else 142 patchShebangs --host "$prefix" 143 fi 144 fi 145}