Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
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] PATH...
15
16# Flags:
17# --build : Lookup commands available at build-time
18# --host : Lookup commands available at runtime
19
20# Example use cases,
21# $ patchShebangs --host /nix/store/...-hello-1.0/bin
22# $ patchShebangs --build configure
23
24patchShebangs() {
25 local pathName
26
27 if [[ "$1" == "--host" ]]; then
28 pathName=HOST_PATH
29 shift
30 elif [[ "$1" == "--build" ]]; then
31 pathName=PATH
32 shift
33 fi
34
35 echo "patching script interpreter paths in $@"
36 local f
37 local oldPath
38 local newPath
39 local arg0
40 local args
41 local oldInterpreterLine
42 local newInterpreterLine
43
44 if [[ $# -eq 0 ]]; then
45 echo "No arguments supplied to patchShebangs" >&2
46 return 0
47 fi
48
49 local f
50 while IFS= read -r -d $'\0' f; do
51 isScript "$f" || continue
52
53 read -r oldInterpreterLine < "$f"
54 read -r oldPath arg0 args <<< "${oldInterpreterLine:2}"
55
56 if [[ -z "$pathName" ]]; then
57 if [[ -n $strictDeps && $f == "$NIX_STORE"* ]]; then
58 pathName=HOST_PATH
59 else
60 pathName=PATH
61 fi
62 fi
63
64 if [[ "$oldPath" == *"/bin/env" ]]; then
65 if [[ $arg0 == "-S" ]]; then
66 arg0=${args%% *}
67 args=${args#* }
68 newPath="$(PATH="${!pathName}" command -v "env" || true)"
69 args="-S $(PATH="${!pathName}" command -v "$arg0" || true) $args"
70
71 # Check for unsupported 'env' functionality:
72 # - options: something starting with a '-' besides '-S'
73 # - environment variables: foo=bar
74 elif [[ $arg0 == "-"* || $arg0 == *"="* ]]; then
75 echo "$f: unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)" >&2
76 exit 1
77 else
78 newPath="$(PATH="${!pathName}" command -v "$arg0" || true)"
79 fi
80 else
81 if [[ -z $oldPath ]]; then
82 # If no interpreter is specified linux will use /bin/sh. Set
83 # oldpath="/bin/sh" so that we get /nix/store/.../sh.
84 oldPath="/bin/sh"
85 fi
86
87 newPath="$(PATH="${!pathName}" command -v "$(basename "$oldPath")" || true)"
88
89 args="$arg0 $args"
90 fi
91
92 # Strip trailing whitespace introduced when no arguments are present
93 newInterpreterLine="$newPath $args"
94 newInterpreterLine=${newInterpreterLine%${newInterpreterLine##*[![:space:]]}}
95
96 if [[ -n "$oldPath" && "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ]]; then
97 if [[ -n "$newPath" && "$newPath" != "$oldPath" ]]; then
98 echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\""
99 # escape the escape chars so that sed doesn't interpret them
100 escapedInterpreterLine=${newInterpreterLine//\\/\\\\}
101
102 # Preserve times, see: https://github.com/NixOS/nixpkgs/pull/33281
103 timestamp=$(stat --printf "%y" "$f")
104 sed -i -e "1 s|.*|#\!$escapedInterpreterLine|" "$f"
105 touch --date "$timestamp" "$f"
106 fi
107 fi
108 done < <(find "$@" -type f -perm -0100 -print0)
109}
110
111patchShebangsAuto () {
112 if [[ -z "${dontPatchShebangs-}" && -e "$prefix" ]]; then
113
114 # Dev output will end up being run on the build platform. An
115 # example case of this is sdl2-config. Otherwise, we can just
116 # use the runtime path (--host).
117 if [[ "$output" != out && "$output" = "$outputDev" ]]; then
118 patchShebangs --build "$prefix"
119 else
120 patchShebangs --host "$prefix"
121 fi
122 fi
123}