From 7bf209db50be1ce57298320e596e8c8fd817c927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Sat, 30 Nov 2013 11:03:35 +0100 Subject: [PATCH] patchShebangs: rewrite ".../bin/env python" to /nix/store/.../python Currently "/usr/bin/env python" is rewritten to "/nix/store/.../env python". That doesn't really improve anything because the interpreter still have to be located in $PATH at runtime. The result is that many nix package expressions do .../bin/env fixup themselves. Instead of everyone having to do this patching locally, add the functionality to the standard environment patchShebangs function so that everyone can benefit. --- pkgs/stdenv/generic/setup.sh | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/pkgs/stdenv/generic/setup.sh b/pkgs/stdenv/generic/setup.sh index a50596985ec..3cc84c9db5e 100644 --- a/pkgs/stdenv/generic/setup.sh +++ b/pkgs/stdenv/generic/setup.sh @@ -655,20 +655,57 @@ patchShebangs() { # Rewrite all script interpreter file names (`#! /path') under the # specified directory tree to paths found in $PATH. E.g., # /bin/sh will be rewritten to /nix/store/-some-bash/bin/sh. + # /usr/bin/env gets special treatment so that ".../bin/env python" is + # rewritten to /nix/store//bin/python. # Interpreters that are already in the store are left untouched. header "patching script interpreter paths" local dir="$1" local f + local oldPath + local newPath + local arg0 + local args + local oldInterpreterLine + local newInterpreterLine + for f in $(find "$dir" -type f -perm +0100); do - local oldPath=$(sed -ne '1 s,^#![ ]*\([^ ]*\).*$,\1,p' "$f") + if [ $(head -1 "$f" | head -c +2) != '#!' ]; then + # missing shebang => not a script + continue + fi + + oldInterpreterLine=$(head -1 "$f" | tail -c +3) + read oldPath arg0 args <<< "$oldInterpreterLine" + + if $(echo "$oldPath" | grep -q "/bin/env$"); then + # Check for unsupported 'env' functionality: + # - options: something starting with a '-' + # - environment variables: foo=bar + if $(echo "$arg0" | grep -q -- "^-.*\|.*=.*"); then + echo "unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)" + exit 1 + fi + newPath="$(command -v "$arg0")" + else + if [ "$oldPath" = "" ]; then + # If no interpreter is specified linux will use /bin/sh. Set + # oldpath="/bin/sh" so that we get /nix/store/.../sh. + oldPath="/bin/sh" + fi + newPath="$(command -v "$(basename "$oldPath")")" + args="$arg0 $args" + fi + + newInterpreterLine="$newPath $args" + if [ -n "$oldPath" -a "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ]; then - local newPath=$(type -P $(basename $oldPath) || true) if [ -n "$newPath" -a "$newPath" != "$oldPath" ]; then - echo "$f: interpreter changed from $oldPath to $newPath" - sed -i -e "1 s,$oldPath,$newPath," "$f" + echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\"" + sed -i -e "1 s|.*|#\!$newInterpreterLine|" "$f" fi fi done + stopNest }