lol
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}