summary refs log tree commit diff
path: root/pkgs/build-support/writers/default.nix
diff options
context:
space:
mode:
authorAdrian <agierakowski@gmail.com>2020-07-25 10:47:20 +0100
committerGitHub <noreply@github.com>2020-07-25 11:47:20 +0200
commita6133b7a396816e3e9248ee657f21e4245607e75 (patch)
treed8a5497cfba7b2fe08980441ff4620e17e9b65ff /pkgs/build-support/writers/default.nix
parenta2ba53f4e130698e7d1ab7f18ebce1883d29d29a (diff)
downloadnixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar.gz
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar.bz2
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar.lz
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar.xz
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.tar.zst
nixpkgs-a6133b7a396816e3e9248ee657f21e4245607e75.zip
writers.makeScriptWriter: fix on Darwin\MacOS (#93757)
* writers.makeScriptWriter: fix on Darwin\MacOS

On Darwin a script cannot be used as an interpreter in a shebang line, which
causes scripts produced with makeScriptWriter (and its derivatives) to fail at
run time if the used interpreter was wrapped with makeWrapper (as in the case
of python3.withPackages).

This commit fixes the problem by detecting if the interpreter is a script
and prepending its shebang to the final interpreter line.

For example if used interpreter is;
```
/nix/store/ynwv137n2650qy39swcflxbcygk5jwv1-python3-3.8.3-env/bin/python
```

which is a script with following shebang:
```
#! /nix/store/knd85yc7iwli8344ghav3zli8d9gril0-bash-4.4-p23/bin/bash -e
```

then the shebang line in the produced script will be
```
#! /nix/store/knd85yc7iwli8344ghav3zli8d9gril0-bash-4.4-p23/bin/bash -e /nix/store/ynwv137n2650qy39swcflxbcygk5jwv1-python3-3.8.3-env/bin/python
```

This works on Darwin since there does not seem to be a limit to the length
of the shabang line and the shebang lines support multiple arguments to
the interpreters (as opposed to linux where the kernel imposes a strict limit
on shebang lengh and everything following the interpreter is passed to it
as a single string).

fixes; #93609
related to: #65351 #11133 (and probably a bunch of others)

NOTE: scripts produced on platforms other than Darwin will remain unmodified
by this PR. However it might worth considering extending this fix to BSD systems
in general. I didn't do it since I have no way of testing it on systems other
than MacOS and linux.

* writers.makeScriptWriter: fix typo in comment

* writers.makeScriptWriter: fail build if interpreter of interpreter is a script
Diffstat (limited to 'pkgs/build-support/writers/default.nix')
-rw-r--r--pkgs/build-support/writers/default.nix25
1 files changed, 24 insertions, 1 deletions
diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix
index 43785546acb..4673b4e6cd8 100644
--- a/pkgs/build-support/writers/default.nix
+++ b/pkgs/build-support/writers/default.nix
@@ -22,7 +22,30 @@ rec {
       inherit interpreter;
       contentPath = content;
     }) ''
-      echo "#! $interpreter" > $out
+      # On darwin a script cannot be used as an interpreter in a shebang but
+      # there doesn't seem to be a limit to the size of shebang and multiple
+      # arguments to the interpreter are allowed.
+      if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter
+      then
+        wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3)
+        # Get first word from the line (note: xargs echo remove leading spaces)
+        wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1)
+
+        if isScript $wrapperInterpreter
+        then
+          echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported."
+          exit 1
+        fi
+
+        # This should work as long as wrapperInterpreter is a shell, which is
+        # the case for programs wrapped with makeWrapper, like
+        # python3.withPackages etc.
+        interpreterLine="$wrapperInterpreterLine $interpreter"
+      else
+        interpreterLine=$interpreter
+      fi
+
+      echo "#! $interpreterLine" > $out
       cat "$contentPath" >> $out
       ${optionalString (check != "") ''
         ${check} $out